update throw exception
add util.load and util.save
This commit is contained in:
		
							parent
							
								
									5faf209709
								
							
						
					
					
						commit
						767d87ac3e
					
				
							
								
								
									
										23
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								go.mod
									
									
									
									
									
								
							@ -3,30 +3,31 @@ module apigo.cc/ai/ai
 | 
				
			|||||||
go 1.22
 | 
					go 1.22
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/dop251/goja v0.0.0-20240828124009-016eb7256539
 | 
						github.com/Masterminds/semver/v3 v3.3.0
 | 
				
			||||||
	github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc
 | 
						github.com/dlclark/regexp2 v1.11.4
 | 
				
			||||||
 | 
						github.com/dop251/base64dec v0.0.0-20231022112746-c6c9f9a96217
 | 
				
			||||||
	github.com/fsnotify/fsnotify v1.7.0
 | 
						github.com/fsnotify/fsnotify v1.7.0
 | 
				
			||||||
	github.com/go-resty/resty/v2 v2.15.0
 | 
						github.com/go-resty/resty/v2 v2.15.2
 | 
				
			||||||
 | 
						github.com/go-sourcemap/sourcemap v2.1.4+incompatible
 | 
				
			||||||
	github.com/golang-jwt/jwt/v5 v5.2.1
 | 
						github.com/golang-jwt/jwt/v5 v5.2.1
 | 
				
			||||||
	github.com/sashabaranov/go-openai v1.29.2
 | 
						github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134
 | 
				
			||||||
 | 
						github.com/sashabaranov/go-openai v1.30.3
 | 
				
			||||||
	github.com/ssgo/config v1.7.7
 | 
						github.com/ssgo/config v1.7.7
 | 
				
			||||||
	github.com/ssgo/httpclient v1.7.7
 | 
						github.com/ssgo/httpclient v1.7.7
 | 
				
			||||||
	github.com/ssgo/log v1.7.7
 | 
						github.com/ssgo/log v1.7.7
 | 
				
			||||||
	github.com/ssgo/u v1.7.7
 | 
						github.com/ssgo/u v1.7.7
 | 
				
			||||||
	github.com/stretchr/testify v1.9.0
 | 
						github.com/stretchr/testify v1.9.0
 | 
				
			||||||
 | 
						go.uber.org/goleak v1.3.0
 | 
				
			||||||
 | 
						golang.org/x/net v0.29.0
 | 
				
			||||||
 | 
						golang.org/x/text v0.18.0
 | 
				
			||||||
 | 
						gopkg.in/yaml.v2 v2.4.0
 | 
				
			||||||
	gopkg.in/yaml.v3 v3.0.1
 | 
						gopkg.in/yaml.v3 v3.0.1
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/davecgh/go-spew v1.1.1 // indirect
 | 
						github.com/davecgh/go-spew v1.1.1 // indirect
 | 
				
			||||||
	github.com/dlclark/regexp2 v1.11.4 // indirect
 | 
						github.com/kr/text v0.2.0 // indirect
 | 
				
			||||||
	github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
 | 
					 | 
				
			||||||
	github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect
 | 
					 | 
				
			||||||
	github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
						github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
				
			||||||
	github.com/ssgo/standard v1.7.7 // indirect
 | 
						github.com/ssgo/standard v1.7.7 // indirect
 | 
				
			||||||
	golang.org/x/net v0.29.0 // indirect
 | 
					 | 
				
			||||||
	golang.org/x/sys v0.25.0 // indirect
 | 
						golang.org/x/sys v0.25.0 // indirect
 | 
				
			||||||
	golang.org/x/text v0.18.0 // indirect
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					 | 
				
			||||||
replace github.com/dop251/goja v0.0.0-20240828124009-016eb7256539 => ./goja
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@ goja
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
ECMAScript 5.1(+) implementation in Go.
 | 
					ECMAScript 5.1(+) implementation in Go.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[](https://pkg.go.dev/github.com/dop251/goja)
 | 
					[](https://pkg.go.dev/apigo.cc/ai/ai/goja)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Goja is an implementation of ECMAScript 5.1 in pure Go with emphasis on standard compliance and
 | 
					Goja is an implementation of ECMAScript 5.1 in pure Go with emphasis on standard compliance and
 | 
				
			||||||
performance.
 | 
					performance.
 | 
				
			||||||
@ -20,7 +20,7 @@ Features
 | 
				
			|||||||
   pass all of them. See .tc39_test262_checkout.sh for the latest working commit id.
 | 
					   pass all of them. See .tc39_test262_checkout.sh for the latest working commit id.
 | 
				
			||||||
 * Capable of running Babel, Typescript compiler and pretty much anything written in ES5.
 | 
					 * Capable of running Babel, Typescript compiler and pretty much anything written in ES5.
 | 
				
			||||||
 * Sourcemaps.
 | 
					 * Sourcemaps.
 | 
				
			||||||
 * Most of ES6 functionality, still work in progress, see https://github.com/dop251/goja/milestone/1?closed=1
 | 
					 * Most of ES6 functionality, still work in progress, see https://apigo.cc/ai/ai/goja/milestone/1?closed=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Known incompatibilities and caveats
 | 
					Known incompatibilities and caveats
 | 
				
			||||||
-----------------------------------
 | 
					-----------------------------------
 | 
				
			||||||
@ -48,7 +48,7 @@ key = undefined; // Now it does
 | 
				
			|||||||
The reason for it is the limitation of the Go runtime. At the time of writing (version 1.15) having a finalizer
 | 
					The reason for it is the limitation of the Go runtime. At the time of writing (version 1.15) having a finalizer
 | 
				
			||||||
set on an object which is part of a reference cycle makes the whole cycle non-garbage-collectable. The solution
 | 
					set on an object which is part of a reference cycle makes the whole cycle non-garbage-collectable. The solution
 | 
				
			||||||
above is the only reasonable way I can think of without involving finalizers. This is the third attempt
 | 
					above is the only reasonable way I can think of without involving finalizers. This is the third attempt
 | 
				
			||||||
(see https://github.com/dop251/goja/issues/250 and https://github.com/dop251/goja/issues/199 for more details).
 | 
					(see https://apigo.cc/ai/ai/goja/issues/250 and https://apigo.cc/ai/ai/goja/issues/199 for more details).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Note, this does not have any effect on the application logic, but may cause a higher-than-expected memory usage.
 | 
					Note, this does not have any effect on the application logic, but may cause a higher-than-expected memory usage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -80,7 +80,7 @@ FAQ
 | 
				
			|||||||
Although it's faster than many scripting language implementations in Go I have seen
 | 
					Although it's faster than many scripting language implementations in Go I have seen
 | 
				
			||||||
(for example it's 6-7 times faster than otto on average) it is not a
 | 
					(for example it's 6-7 times faster than otto on average) it is not a
 | 
				
			||||||
replacement for V8 or SpiderMonkey or any other general-purpose JavaScript engine.
 | 
					replacement for V8 or SpiderMonkey or any other general-purpose JavaScript engine.
 | 
				
			||||||
You can find some benchmarks [here](https://github.com/dop251/goja/issues/2).
 | 
					You can find some benchmarks [here](https://apigo.cc/ai/ai/goja/issues/2).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Why would I want to use it over a V8 wrapper?
 | 
					### Why would I want to use it over a V8 wrapper?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -106,13 +106,13 @@ it's not possible to pass object values between runtimes.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
setTimeout() assumes concurrent execution of code which requires an execution
 | 
					setTimeout() assumes concurrent execution of code which requires an execution
 | 
				
			||||||
environment, for example an event loop similar to nodejs or a browser.
 | 
					environment, for example an event loop similar to nodejs or a browser.
 | 
				
			||||||
There is a [separate project](https://github.com/dop251/goja_nodejs) aimed at providing some NodeJS functionality,
 | 
					There is a [separate project](https://apigo.cc/ai/ai/goja_nodejs) aimed at providing some NodeJS functionality,
 | 
				
			||||||
and it includes an event loop.
 | 
					and it includes an event loop.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Can you implement (feature X from ES6 or higher)?
 | 
					### Can you implement (feature X from ES6 or higher)?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
I will be adding features in their dependency order and as quickly as time permits. Please do not ask
 | 
					I will be adding features in their dependency order and as quickly as time permits. Please do not ask
 | 
				
			||||||
for ETAs. Features that are open in the [milestone](https://github.com/dop251/goja/milestone/1) are either in progress
 | 
					for ETAs. Features that are open in the [milestone](https://apigo.cc/ai/ai/goja/milestone/1) are either in progress
 | 
				
			||||||
or will be worked on next.
 | 
					or will be worked on next.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The ongoing work is done in separate feature branches which are merged into master when appropriate.
 | 
					The ongoing work is done in separate feature branches which are merged into master when appropriate.
 | 
				
			||||||
@ -153,13 +153,13 @@ if num := v.Export().(int64); num != 4 {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Passing Values to JS
 | 
					Passing Values to JS
 | 
				
			||||||
--------------------
 | 
					--------------------
 | 
				
			||||||
Any Go value can be passed to JS using Runtime.ToValue() method. See the method's [documentation](https://pkg.go.dev/github.com/dop251/goja#Runtime.ToValue) for more details.
 | 
					Any Go value can be passed to JS using Runtime.ToValue() method. See the method's [documentation](https://pkg.go.dev/apigo.cc/ai/ai/goja#Runtime.ToValue) for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Exporting Values from JS
 | 
					Exporting Values from JS
 | 
				
			||||||
------------------------
 | 
					------------------------
 | 
				
			||||||
A JS value can be exported into its default Go representation using Value.Export() method.
 | 
					A JS value can be exported into its default Go representation using Value.Export() method.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Alternatively it can be exported into a specific Go variable using [Runtime.ExportTo()](https://pkg.go.dev/github.com/dop251/goja#Runtime.ExportTo) method.
 | 
					Alternatively it can be exported into a specific Go variable using [Runtime.ExportTo()](https://pkg.go.dev/apigo.cc/ai/ai/goja#Runtime.ExportTo) method.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Within a single export operation the same Object will be represented by the same Go value (either the same map, slice or
 | 
					Within a single export operation the same Object will be represented by the same Go value (either the same map, slice or
 | 
				
			||||||
a pointer to the same struct). This includes circular objects and makes it possible to export them.
 | 
					a pointer to the same struct). This includes circular objects and makes it possible to export them.
 | 
				
			||||||
@ -168,7 +168,7 @@ Calling JS functions from Go
 | 
				
			|||||||
----------------------------
 | 
					----------------------------
 | 
				
			||||||
There are 2 approaches:
 | 
					There are 2 approaches:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Using [AssertFunction()](https://pkg.go.dev/github.com/dop251/goja#AssertFunction):
 | 
					- Using [AssertFunction()](https://pkg.go.dev/apigo.cc/ai/ai/goja#AssertFunction):
 | 
				
			||||||
```go
 | 
					```go
 | 
				
			||||||
const SCRIPT = `
 | 
					const SCRIPT = `
 | 
				
			||||||
function sum(a, b) {
 | 
					function sum(a, b) {
 | 
				
			||||||
@ -193,7 +193,7 @@ if err != nil {
 | 
				
			|||||||
fmt.Println(res)
 | 
					fmt.Println(res)
 | 
				
			||||||
// Output: 42
 | 
					// Output: 42
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
- Using [Runtime.ExportTo()](https://pkg.go.dev/github.com/dop251/goja#Runtime.ExportTo):
 | 
					- Using [Runtime.ExportTo()](https://pkg.go.dev/apigo.cc/ai/ai/goja#Runtime.ExportTo):
 | 
				
			||||||
```go
 | 
					```go
 | 
				
			||||||
const SCRIPT = `
 | 
					const SCRIPT = `
 | 
				
			||||||
function sum(a, b) {
 | 
					function sum(a, b) {
 | 
				
			||||||
@ -224,7 +224,7 @@ Mapping struct field and method names
 | 
				
			|||||||
-------------------------------------
 | 
					-------------------------------------
 | 
				
			||||||
By default, the names are passed through as is which means they are capitalised. This does not match
 | 
					By default, the names are passed through as is which means they are capitalised. This does not match
 | 
				
			||||||
the standard JavaScript naming convention, so if you need to make your JS code look more natural or if you are
 | 
					the standard JavaScript naming convention, so if you need to make your JS code look more natural or if you are
 | 
				
			||||||
dealing with a 3rd party library, you can use a [FieldNameMapper](https://pkg.go.dev/github.com/dop251/goja#FieldNameMapper):
 | 
					dealing with a 3rd party library, you can use a [FieldNameMapper](https://pkg.go.dev/apigo.cc/ai/ai/goja#FieldNameMapper):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```go
 | 
					```go
 | 
				
			||||||
vm := goja.New()
 | 
					vm := goja.New()
 | 
				
			||||||
@ -238,14 +238,14 @@ fmt.Println(res.Export())
 | 
				
			|||||||
// Output: 42
 | 
					// Output: 42
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
There are two standard mappers: [TagFieldNameMapper](https://pkg.go.dev/github.com/dop251/goja#TagFieldNameMapper) and
 | 
					There are two standard mappers: [TagFieldNameMapper](https://pkg.go.dev/apigo.cc/ai/ai/goja#TagFieldNameMapper) and
 | 
				
			||||||
[UncapFieldNameMapper](https://pkg.go.dev/github.com/dop251/goja#UncapFieldNameMapper), or you can use your own implementation.
 | 
					[UncapFieldNameMapper](https://pkg.go.dev/apigo.cc/ai/ai/goja#UncapFieldNameMapper), or you can use your own implementation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Native Constructors
 | 
					Native Constructors
 | 
				
			||||||
-------------------
 | 
					-------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
In order to implement a constructor function in Go use `func (goja.ConstructorCall) *goja.Object`.
 | 
					In order to implement a constructor function in Go use `func (goja.ConstructorCall) *goja.Object`.
 | 
				
			||||||
See [Runtime.ToValue()](https://pkg.go.dev/github.com/dop251/goja#Runtime.ToValue) documentation for more details.
 | 
					See [Runtime.ToValue()](https://pkg.go.dev/apigo.cc/ai/ai/goja#Runtime.ToValue) documentation for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Regular Expressions
 | 
					Regular Expressions
 | 
				
			||||||
-------------------
 | 
					-------------------
 | 
				
			||||||
@ -331,4 +331,4 @@ func TestInterrupt(t *testing.T) {
 | 
				
			|||||||
NodeJS Compatibility
 | 
					NodeJS Compatibility
 | 
				
			||||||
--------------------
 | 
					--------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
There is a [separate project](https://github.com/dop251/goja_nodejs) aimed at providing some of the NodeJS functionality.
 | 
					There is a [separate project](https://apigo.cc/ai/ai/goja_nodejs) aimed at providing some of the NodeJS functionality.
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ import (
 | 
				
			|||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type arrayIterObject struct {
 | 
					type arrayIterObject struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ import (
 | 
				
			|||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type sparseArrayItem struct {
 | 
					type sparseArrayItem struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
# ast
 | 
					# ast
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
    import "github.com/dop251/goja/ast"
 | 
					    import "apigo.cc/ai/ai/goja/ast"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Package ast declares types representing a JavaScript AST.
 | 
					Package ast declares types representing a JavaScript AST.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -9,9 +9,9 @@ node types are concerned) and may change in the future.
 | 
				
			|||||||
package ast
 | 
					package ast
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/dop251/goja/file"
 | 
						"apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
	"github.com/dop251/goja/token"
 | 
						"apigo.cc/ai/ai/goja/token"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type PropertyKind string
 | 
					type PropertyKind string
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type valueBigInt big.Int
 | 
					type valueBigInt big.Int
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
package goja
 | 
					package goja
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "github.com/dop251/goja/unistring"
 | 
					import "apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const propNameStack = "stack"
 | 
					const propNameStack = "stack"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@ import (
 | 
				
			|||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"unicode/utf8"
 | 
						"unicode/utf8"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const hexUpper = "0123456789ABCDEF"
 | 
					const hexUpper = "0123456789ABCDEF"
 | 
				
			||||||
 | 
				
			|||||||
@ -12,7 +12,7 @@ import (
 | 
				
			|||||||
	"unicode/utf16"
 | 
						"unicode/utf16"
 | 
				
			||||||
	"unicode/utf8"
 | 
						"unicode/utf8"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const hex = "0123456789abcdef"
 | 
					const hex = "0123456789abcdef"
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ import (
 | 
				
			|||||||
	"math"
 | 
						"math"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/ftoa"
 | 
						"apigo.cc/ai/ai/goja/ftoa"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *Runtime) toNumber(v Value) Value {
 | 
					func (r *Runtime) toNumber(v Value) Value {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
package goja
 | 
					package goja
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -605,7 +605,7 @@ func (r *Runtime) wrapPromiseReaction(fObj *Object) func(interface{}) {
 | 
				
			|||||||
// NewPromise creates and returns a Promise and resolving functions for it.
 | 
					// NewPromise creates and returns a Promise and resolving functions for it.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// WARNING: The returned values are not goroutine-safe and must not be called in parallel with VM running.
 | 
					// WARNING: The returned values are not goroutine-safe and must not be called in parallel with VM running.
 | 
				
			||||||
// In order to make use of this method you need an event loop such as the one in goja_nodejs (https://github.com/dop251/goja_nodejs)
 | 
					// In order to make use of this method you need an event loop such as the one in goja_nodejs (https://apigo.cc/ai/ai/goja_nodejs)
 | 
				
			||||||
// where it can be used like this:
 | 
					// where it can be used like this:
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	loop := NewEventLoop()
 | 
					//	loop := NewEventLoop()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
package goja
 | 
					package goja
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type nativeProxyHandler struct {
 | 
					type nativeProxyHandler struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
package goja
 | 
					package goja
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja/parser"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/dop251/goja/parser"
 | 
					 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"unicode/utf16"
 | 
						"unicode/utf16"
 | 
				
			||||||
 | 
				
			|||||||
@ -7,9 +7,9 @@ import (
 | 
				
			|||||||
	"unicode/utf16"
 | 
						"unicode/utf16"
 | 
				
			||||||
	"unicode/utf8"
 | 
						"unicode/utf8"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/parser"
 | 
						"apigo.cc/ai/ai/goja/parser"
 | 
				
			||||||
	"golang.org/x/text/collate"
 | 
						"golang.org/x/text/collate"
 | 
				
			||||||
	"golang.org/x/text/language"
 | 
						"golang.org/x/text/language"
 | 
				
			||||||
	"golang.org/x/text/unicode/norm"
 | 
						"golang.org/x/text/unicode/norm"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
package goja
 | 
					package goja
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "github.com/dop251/goja/unistring"
 | 
					import "apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	SymHasInstance        = newSymbol(asciiString("Symbol.hasInstance"))
 | 
						SymHasInstance        = newSymbol(asciiString("Symbol.hasInstance"))
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ import (
 | 
				
			|||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"unsafe"
 | 
						"unsafe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type typedArraySortCtx struct {
 | 
					type typedArraySortCtx struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,13 @@
 | 
				
			|||||||
package goja
 | 
					package goja
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja/token"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/dop251/goja/token"
 | 
					 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/ast"
 | 
						"apigo.cc/ai/ai/goja/ast"
 | 
				
			||||||
	"github.com/dop251/goja/file"
 | 
						"apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type blockType int
 | 
					type blockType int
 | 
				
			||||||
 | 
				
			|||||||
@ -3,10 +3,10 @@ package goja
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"math/big"
 | 
						"math/big"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/ast"
 | 
						"apigo.cc/ai/ai/goja/ast"
 | 
				
			||||||
	"github.com/dop251/goja/file"
 | 
						"apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
	"github.com/dop251/goja/token"
 | 
						"apigo.cc/ai/ai/goja/token"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type compiledExpr interface {
 | 
					type compiledExpr interface {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,10 @@
 | 
				
			|||||||
package goja
 | 
					package goja
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/dop251/goja/ast"
 | 
						"apigo.cc/ai/ai/goja/ast"
 | 
				
			||||||
	"github.com/dop251/goja/file"
 | 
						"apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
	"github.com/dop251/goja/token"
 | 
						"apigo.cc/ai/ai/goja/token"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *compiler) compileStatement(v ast.Statement, needResult bool) {
 | 
					func (c *compiler) compileStatement(v ast.Statement, needResult bool) {
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import (
 | 
				
			|||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"unsafe"
 | 
						"unsafe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TESTLIB = `
 | 
					const TESTLIB = `
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
package goja
 | 
					package goja
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
# file
 | 
					# file
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
    import "github.com/dop251/goja/file"
 | 
					    import "apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Package file encapsulates the file abstractions used by the ast & parser.
 | 
					Package file encapsulates the file abstractions used by the ast & parser.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ import (
 | 
				
			|||||||
	"math"
 | 
						"math"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/ftoa/internal/fast"
 | 
						"apigo.cc/ai/ai/goja/ftoa/internal/fast"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type FToStrMode int
 | 
					type FToStrMode int
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										60
									
								
								goja/func.go
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								goja/func.go
									
									
									
									
									
								
							@ -1,11 +1,9 @@
 | 
				
			|||||||
package goja
 | 
					package goja
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type resultType uint8
 | 
					type resultType uint8
 | 
				
			||||||
@ -570,34 +568,34 @@ func (f *nativeFuncObject) vmCall(vm *vm, n int) {
 | 
				
			|||||||
			ret = _undefined
 | 
								ret = _undefined
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		//fmt.Println(">>>>>>>>>>>>>>>>>>>>>>", ret.toString().String())
 | 
							//fmt.Println(">>>>>>>>>>>>>>>>>>>>>>", ret.toString().String())
 | 
				
			||||||
		if strings.HasPrefix(ret.toString().String(), "GoError: ") {
 | 
							//if strings.HasPrefix(ret.toString().String(), "GoError: ") {
 | 
				
			||||||
			//fmt.Println(">>>>>>>>>>>>>>>>>>>>>>", 111)
 | 
							//	//fmt.Println(">>>>>>>>>>>>>>>>>>>>>>", 111)
 | 
				
			||||||
			//vm.throw(Exception{
 | 
							//	//vm.throw(Exception{
 | 
				
			||||||
			//	val: ret.toString(),
 | 
							//	//	val: ret.toString(),
 | 
				
			||||||
			//})
 | 
							//	//})
 | 
				
			||||||
			//stack := make([]StackFrame, 0)
 | 
							//	//stack := make([]StackFrame, 0)
 | 
				
			||||||
			//for i := len(vm.callStack) - 1; i >= 0; i-- {
 | 
							//	//for i := len(vm.callStack) - 1; i >= 0; i-- {
 | 
				
			||||||
			//	frame := &vm.callStack[i]
 | 
							//	//	frame := &vm.callStack[i]
 | 
				
			||||||
			//	if frame.prg != nil || frame.sb > 0 {
 | 
							//	//	if frame.prg != nil || frame.sb > 0 {
 | 
				
			||||||
			//		var funcName unistring.String
 | 
							//	//		var funcName unistring.String
 | 
				
			||||||
			//		if prg := frame.prg; prg != nil {
 | 
							//	//		if prg := frame.prg; prg != nil {
 | 
				
			||||||
			//			funcName = prg.funcName
 | 
							//	//			funcName = prg.funcName
 | 
				
			||||||
			//		} else {
 | 
							//	//		} else {
 | 
				
			||||||
			//			funcName = getFuncName(vm.stack, frame.sb)
 | 
							//	//			funcName = getFuncName(vm.stack, frame.sb)
 | 
				
			||||||
			//		}
 | 
							//	//		}
 | 
				
			||||||
			//		stack = append(stack, StackFrame{prg: vm.callStack[i].prg, pc: frame.pc, funcName: funcName})
 | 
							//	//		stack = append(stack, StackFrame{prg: vm.callStack[i].prg, pc: frame.pc, funcName: funcName})
 | 
				
			||||||
			//		//prg := vm.callStack[i].prg
 | 
							//	//		//prg := vm.callStack[i].prg
 | 
				
			||||||
			//		//stack = append(stack, string(funcName)+" "+prg.src.Position(prg.sourceOffset(frame.pc)).String())
 | 
							//	//		//stack = append(stack, string(funcName)+" "+prg.src.Position(prg.sourceOffset(frame.pc)).String())
 | 
				
			||||||
			//	}
 | 
							//	//	}
 | 
				
			||||||
			//}
 | 
							//	//}
 | 
				
			||||||
			vm.throw(&Exception{
 | 
							//	vm.throw(&Exception{
 | 
				
			||||||
				val: ret.ToString(),
 | 
							//		val: ret.ToString(),
 | 
				
			||||||
			})
 | 
							//	})
 | 
				
			||||||
			return
 | 
							//	return
 | 
				
			||||||
		} else {
 | 
							//} else {
 | 
				
			||||||
			vm.stack[vm.sp-n-2] = ret
 | 
							vm.stack[vm.sp-n-2] = ret
 | 
				
			||||||
			vm.popCtx()
 | 
							vm.popCtx()
 | 
				
			||||||
		}
 | 
							//}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		vm.stack[vm.sp-n-2] = _undefined
 | 
							vm.stack[vm.sp-n-2] = _undefined
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										15
									
								
								goja/go.mod
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								goja/go.mod
									
									
									
									
									
								
							@ -1,15 +0,0 @@
 | 
				
			|||||||
module github.com/dop251/goja
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
go 1.20
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
require (
 | 
					 | 
				
			||||||
	github.com/Masterminds/semver/v3 v3.2.1
 | 
					 | 
				
			||||||
	github.com/dlclark/regexp2 v1.11.4
 | 
					 | 
				
			||||||
	github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d
 | 
					 | 
				
			||||||
	github.com/go-sourcemap/sourcemap v2.1.3+incompatible
 | 
					 | 
				
			||||||
	github.com/google/pprof v0.0.0-20230207041349-798e818bf904
 | 
					 | 
				
			||||||
	golang.org/x/text v0.3.8
 | 
					 | 
				
			||||||
	gopkg.in/yaml.v2 v2.4.0
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
require github.com/kr/pretty v0.3.0 // indirect
 | 
					 | 
				
			||||||
@ -13,9 +13,9 @@ import (
 | 
				
			|||||||
	"runtime/pprof"
 | 
						"runtime/pprof"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja"
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
	"github.com/dop251/goja_nodejs/console"
 | 
						"apigo.cc/ai/ai/goja_nodejs/console"
 | 
				
			||||||
	"github.com/dop251/goja_nodejs/require"
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
 | 
					var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import (
 | 
				
			|||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
package goja
 | 
					package goja
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "github.com/dop251/goja/unistring"
 | 
					import "apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type argumentsObject struct {
 | 
					type argumentsObject struct {
 | 
				
			||||||
	baseObject
 | 
						baseObject
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ import (
 | 
				
			|||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ import (
 | 
				
			|||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type objectGoArrayReflect struct {
 | 
					type objectGoArrayReflect struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@ package goja
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type objectGoMapSimple struct {
 | 
					type objectGoMapSimple struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type objectGoMapReflect struct {
 | 
					type objectGoMapReflect struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -6,8 +6,8 @@ import (
 | 
				
			|||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/parser"
 | 
						"apigo.cc/ai/ai/goja/parser"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// JsonEncodable allows custom JSON encoding by JSON.stringify()
 | 
					// JsonEncodable allows custom JSON encoding by JSON.stringify()
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import (
 | 
				
			|||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type objectGoSlice struct {
 | 
					type objectGoSlice struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ import (
 | 
				
			|||||||
	"math/bits"
 | 
						"math/bits"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type objectGoSliceReflect struct {
 | 
					type objectGoSliceReflect struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
package goja
 | 
					package goja
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
					 | 
				
			||||||
	"math"
 | 
						"math"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,11 @@
 | 
				
			|||||||
# parser
 | 
					# parser
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
    import "github.com/dop251/goja/parser"
 | 
					    import "apigo.cc/ai/ai/goja/parser"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Package parser implements a parser for JavaScript. Borrowed from https://github.com/robertkrimen/otto/tree/master/parser
 | 
					Package parser implements a parser for JavaScript. Borrowed from https://github.com/robertkrimen/otto/tree/master/parser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    import (
 | 
					    import (
 | 
				
			||||||
        "github.com/dop251/goja/parser"
 | 
					        "apigo.cc/ai/ai/goja/parser"
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Parse and return an AST
 | 
					Parse and return an AST
 | 
				
			||||||
 | 
				
			|||||||
@ -4,8 +4,8 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/file"
 | 
						"apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
	"github.com/dop251/goja/token"
 | 
						"apigo.cc/ai/ai/goja/token"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
 | 
				
			|||||||
@ -3,10 +3,10 @@ package parser
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/ast"
 | 
						"apigo.cc/ai/ai/goja/ast"
 | 
				
			||||||
	"github.com/dop251/goja/file"
 | 
						"apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
	"github.com/dop251/goja/token"
 | 
						"apigo.cc/ai/ai/goja/token"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (self *_parser) parseIdentifier() *ast.Identifier {
 | 
					func (self *_parser) parseIdentifier() *ast.Identifier {
 | 
				
			||||||
 | 
				
			|||||||
@ -12,9 +12,9 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/text/unicode/rangetable"
 | 
						"golang.org/x/text/unicode/rangetable"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/file"
 | 
						"apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
	"github.com/dop251/goja/token"
 | 
						"apigo.cc/ai/ai/goja/token"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
 | 
				
			|||||||
@ -3,9 +3,9 @@ package parser
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/file"
 | 
						"apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
	"github.com/dop251/goja/token"
 | 
						"apigo.cc/ai/ai/goja/token"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestLexer(t *testing.T) {
 | 
					func TestLexer(t *testing.T) {
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/ast"
 | 
						"apigo.cc/ai/ai/goja/ast"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func marshal(name string, children ...interface{}) interface{} {
 | 
					func marshal(name string, children ...interface{}) interface{} {
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
Package parser implements a parser for JavaScript.
 | 
					Package parser implements a parser for JavaScript.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	import (
 | 
						import (
 | 
				
			||||||
	    "github.com/dop251/goja/parser"
 | 
						    "apigo.cc/ai/ai/goja/parser"
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Parse and return an AST
 | 
					Parse and return an AST
 | 
				
			||||||
@ -38,10 +38,10 @@ import (
 | 
				
			|||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/ast"
 | 
						"apigo.cc/ai/ai/goja/ast"
 | 
				
			||||||
	"github.com/dop251/goja/file"
 | 
						"apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
	"github.com/dop251/goja/token"
 | 
						"apigo.cc/ai/ai/goja/token"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// A Mode value is a set of flags (or 0). They control optional parser functionality.
 | 
					// A Mode value is a set of flags (or 0). They control optional parser functionality.
 | 
				
			||||||
 | 
				
			|||||||
@ -7,10 +7,10 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/ast"
 | 
						"apigo.cc/ai/ai/goja/ast"
 | 
				
			||||||
	"github.com/dop251/goja/file"
 | 
						"apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
	"github.com/dop251/goja/token"
 | 
						"apigo.cc/ai/ai/goja/token"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func firstErr(err error) error {
 | 
					func firstErr(err error) error {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
package parser
 | 
					package parser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/dop251/goja/ast"
 | 
						"apigo.cc/ai/ai/goja/ast"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type _scope struct {
 | 
					type _scope struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -6,9 +6,9 @@ import (
 | 
				
			|||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/ast"
 | 
						"apigo.cc/ai/ai/goja/ast"
 | 
				
			||||||
	"github.com/dop251/goja/file"
 | 
						"apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
	"github.com/dop251/goja/token"
 | 
						"apigo.cc/ai/ai/goja/token"
 | 
				
			||||||
	"github.com/go-sourcemap/sourcemap"
 | 
						"github.com/go-sourcemap/sourcemap"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Proxy is a Go wrapper around ECMAScript Proxy. Calling Runtime.ToValue() on it
 | 
					// Proxy is a Go wrapper around ECMAScript Proxy. Calling Runtime.ToValue() on it
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
package goja
 | 
					package goja
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/dlclark/regexp2"
 | 
						"github.com/dlclark/regexp2"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
					 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
 | 
				
			|||||||
@ -17,10 +17,10 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/text/collate"
 | 
						"golang.org/x/text/collate"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	js_ast "github.com/dop251/goja/ast"
 | 
						js_ast "apigo.cc/ai/ai/goja/ast"
 | 
				
			||||||
	"github.com/dop251/goja/file"
 | 
						"apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
	"github.com/dop251/goja/parser"
 | 
						"apigo.cc/ai/ai/goja/parser"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ import (
 | 
				
			|||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/parser"
 | 
						"apigo.cc/ai/ai/goja/parser"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestGlobalObjectProto(t *testing.T) {
 | 
					func TestGlobalObjectProto(t *testing.T) {
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"unicode/utf8"
 | 
						"unicode/utf8"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type asciiString string
 | 
					type asciiString string
 | 
				
			||||||
 | 
				
			|||||||
@ -9,8 +9,8 @@ import (
 | 
				
			|||||||
	"unicode/utf16"
 | 
						"unicode/utf16"
 | 
				
			||||||
	"unicode/utf8"
 | 
						"unicode/utf8"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/parser"
 | 
						"apigo.cc/ai/ai/goja/parser"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/text/cases"
 | 
						"golang.org/x/text/cases"
 | 
				
			||||||
	"golang.org/x/text/language"
 | 
						"golang.org/x/text/language"
 | 
				
			||||||
 | 
				
			|||||||
@ -10,8 +10,8 @@ import (
 | 
				
			|||||||
	"unicode/utf16"
 | 
						"unicode/utf16"
 | 
				
			||||||
	"unicode/utf8"
 | 
						"unicode/utf8"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/parser"
 | 
						"apigo.cc/ai/ai/goja/parser"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
	"golang.org/x/text/cases"
 | 
						"golang.org/x/text/cases"
 | 
				
			||||||
	"golang.org/x/text/language"
 | 
						"golang.org/x/text/language"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
# token
 | 
					# token
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
    import "github.com/dop251/goja/token"
 | 
					    import "apigo.cc/ai/ai/goja/token"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Package token defines constants representing the lexical tokens of JavaScript
 | 
					Package token defines constants representing the lexical tokens of JavaScript
 | 
				
			||||||
(ECMA5).
 | 
					(ECMA5).
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"unsafe"
 | 
						"unsafe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type byteOrder bool
 | 
					type byteOrder bool
 | 
				
			||||||
 | 
				
			|||||||
@ -9,8 +9,8 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"unsafe"
 | 
						"unsafe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/ftoa"
 | 
						"apigo.cc/ai/ai/goja/ftoa"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@ import (
 | 
				
			|||||||
	"sync/atomic"
 | 
						"sync/atomic"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
package goja
 | 
					package goja
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/dop251/goja/file"
 | 
						"apigo.cc/ai/ai/goja/file"
 | 
				
			||||||
	"github.com/dop251/goja/parser"
 | 
						"apigo.cc/ai/ai/goja/parser"
 | 
				
			||||||
	"github.com/dop251/goja/unistring"
 | 
						"apigo.cc/ai/ai/goja/unistring"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										13
									
								
								goja_nodejs/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								goja_nodejs/LICENSE
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					Copyright (c) 2016 Dmitry Panov
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 | 
				
			||||||
 | 
					documentation files (the "Software"), to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 | 
				
			||||||
 | 
					permit persons to whom the Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 | 
				
			||||||
 | 
					WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 | 
				
			||||||
 | 
					COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 | 
				
			||||||
 | 
					OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
							
								
								
									
										32
									
								
								goja_nodejs/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								goja_nodejs/README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					Nodejs compatibility library for Goja
 | 
				
			||||||
 | 
					====
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This is a collection of Goja modules that provide nodejs compatibility.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
					    "apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
					    "apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
					    registry := new(require.Registry) // this can be shared by multiple runtimes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    runtime := goja.New()
 | 
				
			||||||
 | 
					    req := registry.Enable(runtime)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    runtime.RunString(`
 | 
				
			||||||
 | 
					    var m = require("./m.js");
 | 
				
			||||||
 | 
					    m.test();
 | 
				
			||||||
 | 
					    `)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m, err := req.Require("./m.js")
 | 
				
			||||||
 | 
					    _, _ = m, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					More modules will be added. Contributions welcome too.
 | 
				
			||||||
							
								
								
									
										83
									
								
								goja_nodejs/assert.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								goja_nodejs/assert.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const assert = {
 | 
				
			||||||
 | 
					    _isSameValue(a, b) {
 | 
				
			||||||
 | 
					        if (a === b) {
 | 
				
			||||||
 | 
					            // Handle +/-0 vs. -/+0
 | 
				
			||||||
 | 
					            return a !== 0 || 1 / a === 1 / b;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Handle NaN vs. NaN
 | 
				
			||||||
 | 
					        return a !== a && b !== b;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _toString(value) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (value === 0 && 1 / value === -Infinity) {
 | 
				
			||||||
 | 
					                return '-0';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return String(value);
 | 
				
			||||||
 | 
					        } catch (err) {
 | 
				
			||||||
 | 
					            if (err.name === 'TypeError') {
 | 
				
			||||||
 | 
					                return Object.prototype.toString.call(value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            throw err;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sameValue(actual, expected, message) {
 | 
				
			||||||
 | 
					        if (assert._isSameValue(actual, expected)) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (message === undefined) {
 | 
				
			||||||
 | 
					            message = '';
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            message += ' ';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        message += 'Expected SameValue(«' + assert._toString(actual) + '», «' + assert._toString(expected) + '») to be true';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        throw new Error(message);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    throws(f, ctor, message) {
 | 
				
			||||||
 | 
					        if (message === undefined) {
 | 
				
			||||||
 | 
					            message = '';
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            message += ' ';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            f();
 | 
				
			||||||
 | 
					        } catch (e) {
 | 
				
			||||||
 | 
					            if (e.constructor !== ctor) {
 | 
				
			||||||
 | 
					                throw new Error(message + "Wrong exception type was thrown: " + e.constructor.name);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        throw new Error(message + "No exception was thrown");
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    throwsNodeError(f, ctor, code, message) {
 | 
				
			||||||
 | 
					        if (message === undefined) {
 | 
				
			||||||
 | 
					            message = '';
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            message += ' ';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            f();
 | 
				
			||||||
 | 
					        } catch (e) {
 | 
				
			||||||
 | 
					            if (e.constructor !== ctor) {
 | 
				
			||||||
 | 
					                throw new Error(message + "Wrong exception type was thrown: " + e.constructor.name);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (e.code !== code) {
 | 
				
			||||||
 | 
					                throw new Error(message + "Wrong exception code was thrown: " + e.code);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        throw new Error(message + "No exception was thrown");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = assert;
 | 
				
			||||||
							
								
								
									
										440
									
								
								goja_nodejs/buffer/buffer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										440
									
								
								goja_nodejs/buffer/buffer.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,440 @@
 | 
				
			|||||||
 | 
					package buffer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"encoding/base64"
 | 
				
			||||||
 | 
						"encoding/hex"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/errors"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/dop251/base64dec"
 | 
				
			||||||
 | 
						"golang.org/x/text/encoding/unicode"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ModuleName = "buffer"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Buffer struct {
 | 
				
			||||||
 | 
						r *goja.Runtime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bufferCtorObj *goja.Object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint8ArrayCtorObj *goja.Object
 | 
				
			||||||
 | 
						uint8ArrayCtor    goja.Constructor
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						symApi = goja.NewSymbol("api")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						reflectTypeArrayBuffer = reflect.TypeOf(goja.ArrayBuffer{})
 | 
				
			||||||
 | 
						reflectTypeString      = reflect.TypeOf("")
 | 
				
			||||||
 | 
						reflectTypeInt         = reflect.TypeOf(int64(0))
 | 
				
			||||||
 | 
						reflectTypeFloat       = reflect.TypeOf(0.0)
 | 
				
			||||||
 | 
						reflectTypeBytes       = reflect.TypeOf(([]byte)(nil))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Enable(runtime *goja.Runtime) {
 | 
				
			||||||
 | 
						runtime.Set("Buffer", require.Require(runtime, ModuleName).ToObject(runtime).Get("Buffer"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Bytes(r *goja.Runtime, v goja.Value) []byte {
 | 
				
			||||||
 | 
						var b []byte
 | 
				
			||||||
 | 
						err := r.ExportTo(v, &b)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return []byte(v.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func mod(r *goja.Runtime) *goja.Object {
 | 
				
			||||||
 | 
						res := r.Get("Buffer")
 | 
				
			||||||
 | 
						if res == nil {
 | 
				
			||||||
 | 
							res = require.Require(r, ModuleName).ToObject(r).Get("Buffer")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m, ok := res.(*goja.Object)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							panic(r.NewTypeError("Could not extract Buffer"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return m
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func api(mod *goja.Object) *Buffer {
 | 
				
			||||||
 | 
						if s := mod.GetSymbol(symApi); s != nil {
 | 
				
			||||||
 | 
							b, _ := s.Export().(*Buffer)
 | 
				
			||||||
 | 
							return b
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetApi(r *goja.Runtime) *Buffer {
 | 
				
			||||||
 | 
						return api(mod(r))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func DecodeBytes(r *goja.Runtime, arg, enc goja.Value) []byte {
 | 
				
			||||||
 | 
						switch arg.ExportType() {
 | 
				
			||||||
 | 
						case reflectTypeArrayBuffer:
 | 
				
			||||||
 | 
							return arg.Export().(goja.ArrayBuffer).Bytes()
 | 
				
			||||||
 | 
						case reflectTypeString:
 | 
				
			||||||
 | 
							var codec StringCodec
 | 
				
			||||||
 | 
							if !goja.IsUndefined(enc) {
 | 
				
			||||||
 | 
								codec = stringCodecs[enc.String()]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if codec == nil {
 | 
				
			||||||
 | 
								codec = utf8Codec
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return codec.DecodeAppend(arg.String(), nil)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							if o, ok := arg.(*goja.Object); ok {
 | 
				
			||||||
 | 
								if o.ExportType() == reflectTypeBytes {
 | 
				
			||||||
 | 
									return o.Export().([]byte)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						panic(errors.NewTypeError(r, errors.ErrCodeInvalidArgType, "The \"data\" argument must be of type string or an instance of Buffer, TypedArray, or DataView."))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func WrapBytes(r *goja.Runtime, data []byte) *goja.Object {
 | 
				
			||||||
 | 
						m := mod(r)
 | 
				
			||||||
 | 
						if api := api(m); api != nil {
 | 
				
			||||||
 | 
							return api.WrapBytes(data)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if from, ok := goja.AssertFunction(m.Get("from")); ok {
 | 
				
			||||||
 | 
							ab := r.NewArrayBuffer(data)
 | 
				
			||||||
 | 
							v, err := from(m, r.ToValue(ab))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return v.ToObject(r)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						panic(r.NewTypeError("Buffer.from is not a function"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// EncodeBytes returns the given byte slice encoded as string with the given encoding. If encoding
 | 
				
			||||||
 | 
					// is not specified or not supported, returns a Buffer that wraps the data.
 | 
				
			||||||
 | 
					func EncodeBytes(r *goja.Runtime, data []byte, enc goja.Value) goja.Value {
 | 
				
			||||||
 | 
						var codec StringCodec
 | 
				
			||||||
 | 
						if !goja.IsUndefined(enc) {
 | 
				
			||||||
 | 
							codec = StringCodecByName(enc.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if codec != nil {
 | 
				
			||||||
 | 
							return r.ToValue(codec.Encode(data))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return WrapBytes(r, data)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Buffer) WrapBytes(data []byte) *goja.Object {
 | 
				
			||||||
 | 
						return b.fromBytes(data)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Buffer) ctor(call goja.ConstructorCall) (res *goja.Object) {
 | 
				
			||||||
 | 
						arg := call.Argument(0)
 | 
				
			||||||
 | 
						switch arg.ExportType() {
 | 
				
			||||||
 | 
						case reflectTypeInt, reflectTypeFloat:
 | 
				
			||||||
 | 
							panic(b.r.NewTypeError("Calling the Buffer constructor with numeric argument is not implemented yet"))
 | 
				
			||||||
 | 
							// TODO implement
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b._from(call.Arguments...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type StringCodec interface {
 | 
				
			||||||
 | 
						DecodeAppend(string, []byte) []byte
 | 
				
			||||||
 | 
						Encode([]byte) string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type hexCodec struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (hexCodec) DecodeAppend(s string, b []byte) []byte {
 | 
				
			||||||
 | 
						l := hex.DecodedLen(len(s))
 | 
				
			||||||
 | 
						dst, res := expandSlice(b, l)
 | 
				
			||||||
 | 
						n, err := hex.Decode(dst, []byte(s))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							res = res[:len(b)+n]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (hexCodec) Encode(b []byte) string {
 | 
				
			||||||
 | 
						return hex.EncodeToString(b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type _utf8Codec struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (_utf8Codec) DecodeAppend(s string, b []byte) []byte {
 | 
				
			||||||
 | 
						r, _ := unicode.UTF8.NewEncoder().String(s)
 | 
				
			||||||
 | 
						dst, res := expandSlice(b, len(r))
 | 
				
			||||||
 | 
						copy(dst, r)
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (_utf8Codec) Encode(b []byte) string {
 | 
				
			||||||
 | 
						r, _ := unicode.UTF8.NewDecoder().Bytes(b)
 | 
				
			||||||
 | 
						return string(r)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type base64Codec struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type base64UrlCodec struct {
 | 
				
			||||||
 | 
						base64Codec
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (base64Codec) DecodeAppend(s string, b []byte) []byte {
 | 
				
			||||||
 | 
						res, _ := Base64DecodeAppend(b, s)
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (base64Codec) Encode(b []byte) string {
 | 
				
			||||||
 | 
						return base64.StdEncoding.EncodeToString(b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (base64UrlCodec) Encode(b []byte) string {
 | 
				
			||||||
 | 
						return base64.RawURLEncoding.EncodeToString(b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var utf8Codec StringCodec = _utf8Codec{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var stringCodecs = map[string]StringCodec{
 | 
				
			||||||
 | 
						"hex":       hexCodec{},
 | 
				
			||||||
 | 
						"utf8":      utf8Codec,
 | 
				
			||||||
 | 
						"utf-8":     utf8Codec,
 | 
				
			||||||
 | 
						"base64":    base64Codec{},
 | 
				
			||||||
 | 
						"base64Url": base64UrlCodec{},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func expandSlice(b []byte, l int) (dst, res []byte) {
 | 
				
			||||||
 | 
						if cap(b)-len(b) < l {
 | 
				
			||||||
 | 
							b1 := make([]byte, len(b)+l)
 | 
				
			||||||
 | 
							copy(b1, b)
 | 
				
			||||||
 | 
							dst = b1[len(b):]
 | 
				
			||||||
 | 
							res = b1
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							dst = b[len(b) : len(b)+l]
 | 
				
			||||||
 | 
							res = b[:len(b)+l]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Base64DecodeAppend(dst []byte, src string) ([]byte, error) {
 | 
				
			||||||
 | 
						l := base64.RawStdEncoding.DecodedLen(len(src))
 | 
				
			||||||
 | 
						d, res := expandSlice(dst, l)
 | 
				
			||||||
 | 
						n, err := base64dec.DecodeBase64(d, src)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res = res[:len(dst)+n]
 | 
				
			||||||
 | 
						return res, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Buffer) fromString(str, enc string) *goja.Object {
 | 
				
			||||||
 | 
						codec := stringCodecs[enc]
 | 
				
			||||||
 | 
						if codec == nil {
 | 
				
			||||||
 | 
							codec = utf8Codec
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b.fromBytes(codec.DecodeAppend(str, nil))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Buffer) fromBytes(data []byte) *goja.Object {
 | 
				
			||||||
 | 
						o, err := b.uint8ArrayCtor(b.bufferCtorObj, b.r.ToValue(b.r.NewArrayBuffer(data)))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return o
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Buffer) _from(args ...goja.Value) *goja.Object {
 | 
				
			||||||
 | 
						if len(args) == 0 {
 | 
				
			||||||
 | 
							panic(errors.NewTypeError(b.r, errors.ErrCodeInvalidArgType, "The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						arg := args[0]
 | 
				
			||||||
 | 
						switch arg.ExportType() {
 | 
				
			||||||
 | 
						case reflectTypeArrayBuffer:
 | 
				
			||||||
 | 
							v, err := b.uint8ArrayCtor(b.bufferCtorObj, args...)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return v
 | 
				
			||||||
 | 
						case reflectTypeString:
 | 
				
			||||||
 | 
							var enc string
 | 
				
			||||||
 | 
							if len(args) > 1 {
 | 
				
			||||||
 | 
								enc = args[1].String()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return b.fromString(arg.String(), enc)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							if o, ok := arg.(*goja.Object); ok {
 | 
				
			||||||
 | 
								if o.ExportType() == reflectTypeBytes {
 | 
				
			||||||
 | 
									bb, _ := o.Export().([]byte)
 | 
				
			||||||
 | 
									a := make([]byte, len(bb))
 | 
				
			||||||
 | 
									copy(a, bb)
 | 
				
			||||||
 | 
									return b.fromBytes(a)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if f, ok := goja.AssertFunction(o.Get("valueOf")); ok {
 | 
				
			||||||
 | 
										valueOf, err := f(o)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											panic(err)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if valueOf != o {
 | 
				
			||||||
 | 
											args[0] = valueOf
 | 
				
			||||||
 | 
											return b._from(args...)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if s := o.GetSymbol(goja.SymToPrimitive); s != nil {
 | 
				
			||||||
 | 
										if f, ok := goja.AssertFunction(s); ok {
 | 
				
			||||||
 | 
											str, err := f(o, b.r.ToValue("string"))
 | 
				
			||||||
 | 
											if err != nil {
 | 
				
			||||||
 | 
												panic(err)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											args[0] = str
 | 
				
			||||||
 | 
											return b._from(args...)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// array-like
 | 
				
			||||||
 | 
								if v := o.Get("length"); v != nil {
 | 
				
			||||||
 | 
									length := int(v.ToInteger())
 | 
				
			||||||
 | 
									a := make([]byte, length)
 | 
				
			||||||
 | 
									for i := 0; i < length; i++ {
 | 
				
			||||||
 | 
										item := o.Get(strconv.Itoa(i))
 | 
				
			||||||
 | 
										if item != nil {
 | 
				
			||||||
 | 
											a[i] = byte(item.ToInteger())
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return b.fromBytes(a)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						panic(errors.NewTypeError(b.r, errors.ErrCodeInvalidArgType, "The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received %s", arg))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Buffer) from(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
						return b._from(call.Arguments...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isNumber(v goja.Value) bool {
 | 
				
			||||||
 | 
						switch v.ExportType() {
 | 
				
			||||||
 | 
						case reflectTypeInt, reflectTypeFloat:
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isString(v goja.Value) bool {
 | 
				
			||||||
 | 
						return v.ExportType() == reflectTypeString
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func StringCodecByName(name string) StringCodec {
 | 
				
			||||||
 | 
						return stringCodecs[name]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Buffer) getStringCodec(enc goja.Value) (codec StringCodec) {
 | 
				
			||||||
 | 
						if !goja.IsUndefined(enc) {
 | 
				
			||||||
 | 
							codec = stringCodecs[enc.String()]
 | 
				
			||||||
 | 
							if codec == nil {
 | 
				
			||||||
 | 
								panic(errors.NewTypeError(b.r, "ERR_UNKNOWN_ENCODING", "Unknown encoding: %s", enc))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							codec = utf8Codec
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Buffer) fill(buf []byte, fill string, enc goja.Value) []byte {
 | 
				
			||||||
 | 
						codec := b.getStringCodec(enc)
 | 
				
			||||||
 | 
						b1 := codec.DecodeAppend(fill, buf[:0])
 | 
				
			||||||
 | 
						if len(b1) > len(buf) {
 | 
				
			||||||
 | 
							return b1[:len(buf)]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i := len(b1); i < len(buf); {
 | 
				
			||||||
 | 
							i += copy(buf[i:], buf[:i])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return buf
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Buffer) alloc(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
						arg0 := call.Argument(0)
 | 
				
			||||||
 | 
						size := -1
 | 
				
			||||||
 | 
						if isNumber(arg0) {
 | 
				
			||||||
 | 
							size = int(arg0.ToInteger())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if size < 0 {
 | 
				
			||||||
 | 
							panic(errors.NewTypeError(b.r, errors.ErrCodeInvalidArgType, "The \"size\" argument must be of type number."))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fill := call.Argument(1)
 | 
				
			||||||
 | 
						buf := make([]byte, size)
 | 
				
			||||||
 | 
						if !goja.IsUndefined(fill) {
 | 
				
			||||||
 | 
							if isString(fill) {
 | 
				
			||||||
 | 
								var enc goja.Value
 | 
				
			||||||
 | 
								if a := call.Argument(2); isString(a) {
 | 
				
			||||||
 | 
									enc = a
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									enc = goja.Undefined()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								buf = b.fill(buf, fill.String(), enc)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								fill = fill.ToNumber()
 | 
				
			||||||
 | 
								if !goja.IsNaN(fill) && !goja.IsInfinity(fill) {
 | 
				
			||||||
 | 
									fillByte := byte(fill.ToInteger())
 | 
				
			||||||
 | 
									if fillByte != 0 {
 | 
				
			||||||
 | 
										for i := range buf {
 | 
				
			||||||
 | 
											buf[i] = fillByte
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b.fromBytes(buf)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Buffer) proto_toString(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
						bb := Bytes(b.r, call.This)
 | 
				
			||||||
 | 
						codec := b.getStringCodec(call.Argument(0))
 | 
				
			||||||
 | 
						return b.r.ToValue(codec.Encode(bb))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Buffer) proto_equals(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
						bb := Bytes(b.r, call.This)
 | 
				
			||||||
 | 
						other := call.Argument(0)
 | 
				
			||||||
 | 
						if b.r.InstanceOf(other, b.uint8ArrayCtorObj) {
 | 
				
			||||||
 | 
							otherBytes := Bytes(b.r, other)
 | 
				
			||||||
 | 
							return b.r.ToValue(bytes.Equal(bb, otherBytes))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						panic(errors.NewTypeError(b.r, errors.ErrCodeInvalidArgType, "The \"otherBuffer\" argument must be an instance of Buffer or Uint8Array."))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Require(runtime *goja.Runtime, module *goja.Object) {
 | 
				
			||||||
 | 
						b := &Buffer{r: runtime}
 | 
				
			||||||
 | 
						uint8Array := runtime.Get("Uint8Array")
 | 
				
			||||||
 | 
						if c, ok := goja.AssertConstructor(uint8Array); ok {
 | 
				
			||||||
 | 
							b.uint8ArrayCtor = c
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							panic(runtime.NewTypeError("Uint8Array is not a constructor"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						uint8ArrayObj := uint8Array.ToObject(runtime)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctor := runtime.ToValue(b.ctor).ToObject(runtime)
 | 
				
			||||||
 | 
						ctor.SetPrototype(uint8ArrayObj)
 | 
				
			||||||
 | 
						ctor.DefineDataPropertySymbol(symApi, runtime.ToValue(b), goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_FALSE)
 | 
				
			||||||
 | 
						b.bufferCtorObj = ctor
 | 
				
			||||||
 | 
						b.uint8ArrayCtorObj = uint8ArrayObj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						proto := runtime.NewObject()
 | 
				
			||||||
 | 
						proto.SetPrototype(uint8ArrayObj.Get("prototype").ToObject(runtime))
 | 
				
			||||||
 | 
						proto.DefineDataProperty("constructor", ctor, goja.FLAG_TRUE, goja.FLAG_TRUE, goja.FLAG_FALSE)
 | 
				
			||||||
 | 
						proto.Set("equals", b.proto_equals)
 | 
				
			||||||
 | 
						proto.Set("toString", b.proto_toString)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctor.Set("prototype", proto)
 | 
				
			||||||
 | 
						ctor.Set("poolSize", 8192)
 | 
				
			||||||
 | 
						ctor.Set("from", b.from)
 | 
				
			||||||
 | 
						ctor.Set("alloc", b.alloc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exports := module.Get("exports").(*goja.Object)
 | 
				
			||||||
 | 
						exports.Set("Buffer", ctor)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						require.RegisterCoreModule(ModuleName, Require)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										228
									
								
								goja_nodejs/buffer/buffer_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								goja_nodejs/buffer/buffer_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,228 @@
 | 
				
			|||||||
 | 
					package buffer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestBufferFrom(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
						new(require.Registry).Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := vm.RunString(`
 | 
				
			||||||
 | 
						const Buffer = require("node:buffer").Buffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function checkBuffer(buf) {
 | 
				
			||||||
 | 
							if (!(buf instanceof Buffer)) {
 | 
				
			||||||
 | 
								throw new Error("instanceof Buffer");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
							if (!(buf instanceof Uint8Array)) {
 | 
				
			||||||
 | 
								throw new Error("instanceof Uint8Array");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						checkBuffer(Buffer.from(new ArrayBuffer(16)));
 | 
				
			||||||
 | 
						checkBuffer(Buffer.from(new Uint16Array(8)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const b = Buffer.from("\xff\xfe\xfd");
 | 
				
			||||||
 | 
							const h = b.toString("hex")
 | 
				
			||||||
 | 
							if (h !== "c3bfc3bec3bd") {
 | 
				
			||||||
 | 
								throw new Error(h);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const b = Buffer.from("0102fffdXXX", "hex");
 | 
				
			||||||
 | 
							checkBuffer(b);
 | 
				
			||||||
 | 
							if (b.toString("hex") !== "0102fffd") {
 | 
				
			||||||
 | 
								throw new Error(b.toString("hex"));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const b = Buffer.from('1ag123', 'hex');
 | 
				
			||||||
 | 
							if (b.length !== 1 || b[0] !== 0x1a) {
 | 
				
			||||||
 | 
								throw new Error(b);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const b = Buffer.from('1a7', 'hex');
 | 
				
			||||||
 | 
							if (b.length !== 1 || b[0] !== 0x1a) {
 | 
				
			||||||
 | 
								throw new Error(b);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const b = Buffer.from("\uD801", "utf-8");
 | 
				
			||||||
 | 
							if (b.length !== 3 || b[0] !== 0xef || b[1] !== 0xbf || b[2] !== 0xbd) {
 | 
				
			||||||
 | 
								throw new Error(b);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestFromBase64(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
						new(require.Registry).Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := vm.RunString(`
 | 
				
			||||||
 | 
						const Buffer = require("node:buffer").Buffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							let b = Buffer.from("AAA_", "base64");
 | 
				
			||||||
 | 
							if (b.length !== 3 || b[0] !== 0 || b[1] !== 0 || b[2] !== 0x3f) {
 | 
				
			||||||
 | 
								throw new Error(b.toString("hex"));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let r = b.toString("base64");
 | 
				
			||||||
 | 
							if (r !== "AAA/") {
 | 
				
			||||||
 | 
								throw new Error("to base64: " + r);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for (let i = 0; i < 20; i++) {
 | 
				
			||||||
 | 
								let s = "A".repeat(i) + "_" + "A".repeat(20-i);
 | 
				
			||||||
 | 
								let s1 = "A".repeat(i) + "/" + "A".repeat(20-i);
 | 
				
			||||||
 | 
								let b = Buffer.from(s, "base64");
 | 
				
			||||||
 | 
								let b1 = Buffer.from(s1, "base64");
 | 
				
			||||||
 | 
								if (!b.equals(b1)) {
 | 
				
			||||||
 | 
									throw new Error(s);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							let b = Buffer.from("SQ==???", "base64");
 | 
				
			||||||
 | 
							if (b.length !== 1 || b[0] != 0x49) {
 | 
				
			||||||
 | 
								throw new Error(b.toString("hex"));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							let s = Buffer.from("AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", "base64Url").toString("base64");
 | 
				
			||||||
 | 
							if (s !== "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ+EstJQLr/T+1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow==") {
 | 
				
			||||||
 | 
								throw new Error(s);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestWrapBytes(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
						new(require.Registry).Enable(vm)
 | 
				
			||||||
 | 
						b := []byte{1, 2, 3}
 | 
				
			||||||
 | 
						buffer := GetApi(vm)
 | 
				
			||||||
 | 
						vm.Set("b", buffer.WrapBytes(b))
 | 
				
			||||||
 | 
						Enable(vm)
 | 
				
			||||||
 | 
						_, err := vm.RunString(`
 | 
				
			||||||
 | 
							if (typeof Buffer !== "function") {
 | 
				
			||||||
 | 
								throw new Error("Buffer is not a function: " + typeof Buffer);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (!(b instanceof Buffer)) {
 | 
				
			||||||
 | 
								throw new Error("instanceof Buffer");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (b.toString("hex") !== "010203") {
 | 
				
			||||||
 | 
								throw new Error(b);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestBuffer_alloc(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
						new(require.Registry).Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := vm.RunString(`
 | 
				
			||||||
 | 
						const Buffer = require("node:buffer").Buffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const b = Buffer.alloc(2, "abc");
 | 
				
			||||||
 | 
							if (b.toString() !== "ab") {
 | 
				
			||||||
 | 
								throw new Error(b);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const b = Buffer.alloc(16, "abc");
 | 
				
			||||||
 | 
							if (b.toString() !== "abcabcabcabcabca") {
 | 
				
			||||||
 | 
								throw new Error(b);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const fill = {
 | 
				
			||||||
 | 
								valueOf() {
 | 
				
			||||||
 | 
									return 0xac;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							const b = Buffer.alloc(8, fill);
 | 
				
			||||||
 | 
							if (b.toString("hex") !== "acacacacacacacac") {
 | 
				
			||||||
 | 
								throw new Error(b);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const fill = {
 | 
				
			||||||
 | 
								valueOf() {
 | 
				
			||||||
 | 
									return Infinity;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							const b = Buffer.alloc(2, fill);
 | 
				
			||||||
 | 
							if (b.toString("hex") !== "0000") {
 | 
				
			||||||
 | 
								throw new Error(b);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const fill = {
 | 
				
			||||||
 | 
								valueOf() {
 | 
				
			||||||
 | 
									return "ac";
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							const b = Buffer.alloc(2, fill);
 | 
				
			||||||
 | 
							if (b.toString("hex") !== "0000") {
 | 
				
			||||||
 | 
								throw new Error(b);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const b = Buffer.alloc(2, -257.4);
 | 
				
			||||||
 | 
							if (b.toString("hex") !== "ffff") {
 | 
				
			||||||
 | 
								throw new Error(b);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const b = Buffer.alloc(2, Infinity);
 | 
				
			||||||
 | 
							if (b.toString("hex") !== "0000") {
 | 
				
			||||||
 | 
								throw new Error("Infinity: " + b.toString("hex"));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const b = Buffer.alloc(2, null);
 | 
				
			||||||
 | 
							if (b.toString("hex") !== "0000") {
 | 
				
			||||||
 | 
								throw new Error("Infinity: " + b.toString("hex"));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										72
									
								
								goja_nodejs/console/module.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								goja_nodejs/console/module.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					package console
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/util"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ModuleName = "console"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Console struct {
 | 
				
			||||||
 | 
						runtime *goja.Runtime
 | 
				
			||||||
 | 
						util    *goja.Object
 | 
				
			||||||
 | 
						printer Printer
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Printer interface {
 | 
				
			||||||
 | 
						Log(string)
 | 
				
			||||||
 | 
						Warn(string)
 | 
				
			||||||
 | 
						Error(string)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Console) log(p func(string)) func(goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
						return func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							if format, ok := goja.AssertFunction(c.util.Get("format")); ok {
 | 
				
			||||||
 | 
								ret, err := format(c.util, call.Arguments...)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									panic(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								p(ret.String())
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								panic(c.runtime.NewTypeError("util.format is not a function"))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Require(runtime *goja.Runtime, module *goja.Object) {
 | 
				
			||||||
 | 
						requireWithPrinter(defaultStdPrinter)(runtime, module)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RequireWithPrinter(printer Printer) require.ModuleLoader {
 | 
				
			||||||
 | 
						return requireWithPrinter(printer)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func requireWithPrinter(printer Printer) require.ModuleLoader {
 | 
				
			||||||
 | 
						return func(runtime *goja.Runtime, module *goja.Object) {
 | 
				
			||||||
 | 
							c := &Console{
 | 
				
			||||||
 | 
								runtime: runtime,
 | 
				
			||||||
 | 
								printer: printer,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.util = require.Require(runtime, util.ModuleName).(*goja.Object)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							o := module.Get("exports").(*goja.Object)
 | 
				
			||||||
 | 
							o.Set("log", c.log(c.printer.Log))
 | 
				
			||||||
 | 
							o.Set("error", c.log(c.printer.Error))
 | 
				
			||||||
 | 
							o.Set("warn", c.log(c.printer.Warn))
 | 
				
			||||||
 | 
							o.Set("info", c.log(c.printer.Log))
 | 
				
			||||||
 | 
							o.Set("debug", c.log(c.printer.Log))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Enable(runtime *goja.Runtime) {
 | 
				
			||||||
 | 
						runtime.Set("console", require.Require(runtime, ModuleName))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						require.RegisterCoreModule(ModuleName, Require)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										78
									
								
								goja_nodejs/console/module_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								goja_nodejs/console/module_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					package console
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestConsole(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						new(require.Registry).Enable(vm)
 | 
				
			||||||
 | 
						Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c := vm.Get("console"); c == nil {
 | 
				
			||||||
 | 
							t.Fatal("console not found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := vm.RunString("console.log('')"); err != nil {
 | 
				
			||||||
 | 
							t.Fatal("console.log() error", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := vm.RunString("console.error('')"); err != nil {
 | 
				
			||||||
 | 
							t.Fatal("console.error() error", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := vm.RunString("console.warn('')"); err != nil {
 | 
				
			||||||
 | 
							t.Fatal("console.warn() error", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := vm.RunString("console.info('')"); err != nil {
 | 
				
			||||||
 | 
							t.Fatal("console.info() error", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := vm.RunString("console.debug('')"); err != nil {
 | 
				
			||||||
 | 
							t.Fatal("console.debug() error", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestConsoleWithPrinter(t *testing.T) {
 | 
				
			||||||
 | 
						var stdoutStr, stderrStr string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printer := StdPrinter{
 | 
				
			||||||
 | 
							StdoutPrint: func(s string) { stdoutStr += s },
 | 
				
			||||||
 | 
							StderrPrint: func(s string) { stderrStr += s },
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						registry := new(require.Registry)
 | 
				
			||||||
 | 
						registry.Enable(vm)
 | 
				
			||||||
 | 
						registry.RegisterNativeModule(ModuleName, RequireWithPrinter(printer))
 | 
				
			||||||
 | 
						Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c := vm.Get("console"); c == nil {
 | 
				
			||||||
 | 
							t.Fatal("console not found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := vm.RunString(`
 | 
				
			||||||
 | 
							console.log('a')
 | 
				
			||||||
 | 
							console.error('b')
 | 
				
			||||||
 | 
							console.warn('c')
 | 
				
			||||||
 | 
							console.debug('d')
 | 
				
			||||||
 | 
							console.info('e')
 | 
				
			||||||
 | 
						`)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if want := "ade"; stdoutStr != want {
 | 
				
			||||||
 | 
							t.Fatalf("Unexpected stdout output: got %q, want %q", stdoutStr, want)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if want := "bc"; stderrStr != want {
 | 
				
			||||||
 | 
							t.Fatalf("Unexpected stderr output: got %q, want %q", stderrStr, want)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								goja_nodejs/console/std_printer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								goja_nodejs/console/std_printer.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					package console
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						stderrLogger = log.Default() // the default logger output to stderr
 | 
				
			||||||
 | 
						stdoutLogger = log.New(os.Stdout, "", log.LstdFlags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defaultStdPrinter Printer = &StdPrinter{
 | 
				
			||||||
 | 
							StdoutPrint: func(s string) { stdoutLogger.Print(s) },
 | 
				
			||||||
 | 
							StderrPrint: func(s string) { stderrLogger.Print(s) },
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StdPrinter implements the console.Printer interface
 | 
				
			||||||
 | 
					// that prints to the stdout or stderr.
 | 
				
			||||||
 | 
					type StdPrinter struct {
 | 
				
			||||||
 | 
						StdoutPrint func(s string)
 | 
				
			||||||
 | 
						StderrPrint func(s string)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Log prints s to the stdout.
 | 
				
			||||||
 | 
					func (p StdPrinter) Log(s string) {
 | 
				
			||||||
 | 
						p.StdoutPrint(s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Warn prints s to the stderr.
 | 
				
			||||||
 | 
					func (p StdPrinter) Warn(s string) {
 | 
				
			||||||
 | 
						p.StderrPrint(s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Error prints s to the stderr.
 | 
				
			||||||
 | 
					func (p StdPrinter) Error(s string) {
 | 
				
			||||||
 | 
						p.StderrPrint(s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										71
									
								
								goja_nodejs/errors/errors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								goja_nodejs/errors/errors.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					package errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						ErrCodeInvalidArgType  = "ERR_INVALID_ARG_TYPE"
 | 
				
			||||||
 | 
						ErrCodeInvalidArgValue = "ERR_INVALID_ARG_VALUE"
 | 
				
			||||||
 | 
						ErrCodeInvalidThis     = "ERR_INVALID_THIS"
 | 
				
			||||||
 | 
						ErrCodeMissingArgs     = "ERR_MISSING_ARGS"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func error_toString(call goja.FunctionCall, r *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
						this := call.This.ToObject(r)
 | 
				
			||||||
 | 
						var name, msg string
 | 
				
			||||||
 | 
						if n := this.Get("name"); n != nil && !goja.IsUndefined(n) {
 | 
				
			||||||
 | 
							name = n.String()
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							name = "Error"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if m := this.Get("message"); m != nil && !goja.IsUndefined(m) {
 | 
				
			||||||
 | 
							msg = m.String()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if code := this.Get("code"); code != nil && !goja.IsUndefined(code) {
 | 
				
			||||||
 | 
							if name != "" {
 | 
				
			||||||
 | 
								name += " "
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							name += "[" + code.String() + "]"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if msg != "" {
 | 
				
			||||||
 | 
							if name != "" {
 | 
				
			||||||
 | 
								name += ": "
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							name += msg
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return r.ToValue(name)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func addProps(r *goja.Runtime, e *goja.Object, code string) {
 | 
				
			||||||
 | 
						e.Set("code", code)
 | 
				
			||||||
 | 
						e.DefineDataProperty("toString", r.ToValue(error_toString), goja.FLAG_TRUE, goja.FLAG_TRUE, goja.FLAG_FALSE)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewTypeError(r *goja.Runtime, code string, params ...interface{}) *goja.Object {
 | 
				
			||||||
 | 
						e := r.NewTypeError(params...)
 | 
				
			||||||
 | 
						addProps(r, e, code)
 | 
				
			||||||
 | 
						return e
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewError(r *goja.Runtime, ctor *goja.Object, code string, args ...interface{}) *goja.Object {
 | 
				
			||||||
 | 
						if ctor == nil {
 | 
				
			||||||
 | 
							ctor, _ = r.Get("Error").(*goja.Object)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ctor == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						msg := ""
 | 
				
			||||||
 | 
						if len(args) > 0 {
 | 
				
			||||||
 | 
							f, _ := args[0].(string)
 | 
				
			||||||
 | 
							msg = fmt.Sprintf(f, args[1:]...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						o, err := r.New(ctor, r.ToValue(msg))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						addProps(r, o, code)
 | 
				
			||||||
 | 
						return o
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										514
									
								
								goja_nodejs/eventloop/eventloop.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										514
									
								
								goja_nodejs/eventloop/eventloop.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,514 @@
 | 
				
			|||||||
 | 
					package eventloop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"sync/atomic"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/console"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type job struct {
 | 
				
			||||||
 | 
						cancel func() bool
 | 
				
			||||||
 | 
						fn     func()
 | 
				
			||||||
 | 
						idx    int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cancelled bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Timer struct {
 | 
				
			||||||
 | 
						job
 | 
				
			||||||
 | 
						timer *time.Timer
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Interval struct {
 | 
				
			||||||
 | 
						job
 | 
				
			||||||
 | 
						ticker   *time.Ticker
 | 
				
			||||||
 | 
						stopChan chan struct{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Immediate struct {
 | 
				
			||||||
 | 
						job
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type EventLoop struct {
 | 
				
			||||||
 | 
						vm       *goja.Runtime
 | 
				
			||||||
 | 
						jobChan  chan func()
 | 
				
			||||||
 | 
						jobs     []*job
 | 
				
			||||||
 | 
						jobCount int32
 | 
				
			||||||
 | 
						canRun   int32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auxJobsLock sync.Mutex
 | 
				
			||||||
 | 
						wakeupChan  chan struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auxJobsSpare, auxJobs []func()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stopLock   sync.Mutex
 | 
				
			||||||
 | 
						stopCond   *sync.Cond
 | 
				
			||||||
 | 
						running    bool
 | 
				
			||||||
 | 
						terminated bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enableConsole bool
 | 
				
			||||||
 | 
						registry      *require.Registry
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewEventLoop(opts ...Option) *EventLoop {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop := &EventLoop{
 | 
				
			||||||
 | 
							vm:            vm,
 | 
				
			||||||
 | 
							jobChan:       make(chan func()),
 | 
				
			||||||
 | 
							wakeupChan:    make(chan struct{}, 1),
 | 
				
			||||||
 | 
							enableConsole: true,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.stopCond = sync.NewCond(&loop.stopLock)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, opt := range opts {
 | 
				
			||||||
 | 
							opt(loop)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if loop.registry == nil {
 | 
				
			||||||
 | 
							loop.registry = new(require.Registry)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.registry.Enable(vm)
 | 
				
			||||||
 | 
						if loop.enableConsole {
 | 
				
			||||||
 | 
							console.Enable(vm)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						vm.Set("setTimeout", loop.setTimeout)
 | 
				
			||||||
 | 
						vm.Set("setInterval", loop.setInterval)
 | 
				
			||||||
 | 
						vm.Set("setImmediate", loop.setImmediate)
 | 
				
			||||||
 | 
						vm.Set("clearTimeout", loop.clearTimeout)
 | 
				
			||||||
 | 
						vm.Set("clearInterval", loop.clearInterval)
 | 
				
			||||||
 | 
						vm.Set("clearImmediate", loop.clearImmediate)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return loop
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Option func(*EventLoop)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// EnableConsole controls whether the "console" module is loaded into
 | 
				
			||||||
 | 
					// the runtime used by the loop.  By default, loops are created with
 | 
				
			||||||
 | 
					// the "console" module loaded, pass EnableConsole(false) to
 | 
				
			||||||
 | 
					// NewEventLoop to disable this behavior.
 | 
				
			||||||
 | 
					func EnableConsole(enableConsole bool) Option {
 | 
				
			||||||
 | 
						return func(loop *EventLoop) {
 | 
				
			||||||
 | 
							loop.enableConsole = enableConsole
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func WithRegistry(registry *require.Registry) Option {
 | 
				
			||||||
 | 
						return func(loop *EventLoop) {
 | 
				
			||||||
 | 
							loop.registry = registry
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) schedule(call goja.FunctionCall, repeating bool) goja.Value {
 | 
				
			||||||
 | 
						if fn, ok := goja.AssertFunction(call.Argument(0)); ok {
 | 
				
			||||||
 | 
							delay := call.Argument(1).ToInteger()
 | 
				
			||||||
 | 
							var args []goja.Value
 | 
				
			||||||
 | 
							if len(call.Arguments) > 2 {
 | 
				
			||||||
 | 
								args = append(args, call.Arguments[2:]...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							f := func() { fn(nil, args...) }
 | 
				
			||||||
 | 
							loop.jobCount++
 | 
				
			||||||
 | 
							var job *job
 | 
				
			||||||
 | 
							var ret goja.Value
 | 
				
			||||||
 | 
							if repeating {
 | 
				
			||||||
 | 
								interval := loop.newInterval(f)
 | 
				
			||||||
 | 
								interval.start(loop, time.Duration(delay)*time.Millisecond)
 | 
				
			||||||
 | 
								job = &interval.job
 | 
				
			||||||
 | 
								ret = loop.vm.ToValue(interval)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								timeout := loop.newTimeout(f)
 | 
				
			||||||
 | 
								timeout.start(loop, time.Duration(delay)*time.Millisecond)
 | 
				
			||||||
 | 
								job = &timeout.job
 | 
				
			||||||
 | 
								ret = loop.vm.ToValue(timeout)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							job.idx = len(loop.jobs)
 | 
				
			||||||
 | 
							loop.jobs = append(loop.jobs, job)
 | 
				
			||||||
 | 
							return ret
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) setTimeout(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
						return loop.schedule(call, false)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) setInterval(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
						return loop.schedule(call, true)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) setImmediate(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
						if fn, ok := goja.AssertFunction(call.Argument(0)); ok {
 | 
				
			||||||
 | 
							var args []goja.Value
 | 
				
			||||||
 | 
							if len(call.Arguments) > 1 {
 | 
				
			||||||
 | 
								args = append(args, call.Arguments[1:]...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							f := func() { fn(nil, args...) }
 | 
				
			||||||
 | 
							loop.jobCount++
 | 
				
			||||||
 | 
							return loop.vm.ToValue(loop.addImmediate(f))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetTimeout schedules to run the specified function in the context
 | 
				
			||||||
 | 
					// of the loop as soon as possible after the specified timeout period.
 | 
				
			||||||
 | 
					// SetTimeout returns a Timer which can be passed to ClearTimeout.
 | 
				
			||||||
 | 
					// The instance of goja.Runtime that is passed to the function and any Values derived
 | 
				
			||||||
 | 
					// from it must not be used outside the function. SetTimeout is
 | 
				
			||||||
 | 
					// safe to call inside or outside the loop.
 | 
				
			||||||
 | 
					// If the loop is terminated (see Terminate()) returns nil.
 | 
				
			||||||
 | 
					func (loop *EventLoop) SetTimeout(fn func(*goja.Runtime), timeout time.Duration) *Timer {
 | 
				
			||||||
 | 
						t := loop.newTimeout(func() { fn(loop.vm) })
 | 
				
			||||||
 | 
						if loop.addAuxJob(func() {
 | 
				
			||||||
 | 
							t.start(loop, timeout)
 | 
				
			||||||
 | 
							loop.jobCount++
 | 
				
			||||||
 | 
							t.idx = len(loop.jobs)
 | 
				
			||||||
 | 
							loop.jobs = append(loop.jobs, &t.job)
 | 
				
			||||||
 | 
						}) {
 | 
				
			||||||
 | 
							return t
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ClearTimeout cancels a Timer returned by SetTimeout if it has not run yet.
 | 
				
			||||||
 | 
					// ClearTimeout is safe to call inside or outside the loop.
 | 
				
			||||||
 | 
					func (loop *EventLoop) ClearTimeout(t *Timer) {
 | 
				
			||||||
 | 
						loop.addAuxJob(func() {
 | 
				
			||||||
 | 
							loop.clearTimeout(t)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetInterval schedules to repeatedly run the specified function in
 | 
				
			||||||
 | 
					// the context of the loop as soon as possible after every specified
 | 
				
			||||||
 | 
					// timeout period.  SetInterval returns an Interval which can be
 | 
				
			||||||
 | 
					// passed to ClearInterval. The instance of goja.Runtime that is passed to the
 | 
				
			||||||
 | 
					// function and any Values derived from it must not be used outside
 | 
				
			||||||
 | 
					// the function. SetInterval is safe to call inside or outside the
 | 
				
			||||||
 | 
					// loop.
 | 
				
			||||||
 | 
					// If the loop is terminated (see Terminate()) returns nil.
 | 
				
			||||||
 | 
					func (loop *EventLoop) SetInterval(fn func(*goja.Runtime), timeout time.Duration) *Interval {
 | 
				
			||||||
 | 
						i := loop.newInterval(func() { fn(loop.vm) })
 | 
				
			||||||
 | 
						if loop.addAuxJob(func() {
 | 
				
			||||||
 | 
							i.start(loop, timeout)
 | 
				
			||||||
 | 
							loop.jobCount++
 | 
				
			||||||
 | 
							i.idx = len(loop.jobs)
 | 
				
			||||||
 | 
							loop.jobs = append(loop.jobs, &i.job)
 | 
				
			||||||
 | 
						}) {
 | 
				
			||||||
 | 
							return i
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ClearInterval cancels an Interval returned by SetInterval.
 | 
				
			||||||
 | 
					// ClearInterval is safe to call inside or outside the loop.
 | 
				
			||||||
 | 
					func (loop *EventLoop) ClearInterval(i *Interval) {
 | 
				
			||||||
 | 
						loop.addAuxJob(func() {
 | 
				
			||||||
 | 
							loop.clearInterval(i)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) setRunning() {
 | 
				
			||||||
 | 
						loop.stopLock.Lock()
 | 
				
			||||||
 | 
						defer loop.stopLock.Unlock()
 | 
				
			||||||
 | 
						if loop.running {
 | 
				
			||||||
 | 
							panic("Loop is already started")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.running = true
 | 
				
			||||||
 | 
						atomic.StoreInt32(&loop.canRun, 1)
 | 
				
			||||||
 | 
						loop.auxJobsLock.Lock()
 | 
				
			||||||
 | 
						loop.terminated = false
 | 
				
			||||||
 | 
						loop.auxJobsLock.Unlock()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Run calls the specified function, starts the event loop and waits until there are no more delayed jobs to run
 | 
				
			||||||
 | 
					// after which it stops the loop and returns.
 | 
				
			||||||
 | 
					// The instance of goja.Runtime that is passed to the function and any Values derived from it must not be used
 | 
				
			||||||
 | 
					// outside the function.
 | 
				
			||||||
 | 
					// Do NOT use this function while the loop is already running. Use RunOnLoop() instead.
 | 
				
			||||||
 | 
					// If the loop is already started it will panic.
 | 
				
			||||||
 | 
					func (loop *EventLoop) Run(fn func(*goja.Runtime)) {
 | 
				
			||||||
 | 
						loop.setRunning()
 | 
				
			||||||
 | 
						fn(loop.vm)
 | 
				
			||||||
 | 
						loop.run(false)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Start the event loop in the background. The loop continues to run until Stop() is called.
 | 
				
			||||||
 | 
					// If the loop is already started it will panic.
 | 
				
			||||||
 | 
					func (loop *EventLoop) Start() {
 | 
				
			||||||
 | 
						loop.setRunning()
 | 
				
			||||||
 | 
						go loop.run(true)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StartInForeground starts the event loop in the current goroutine. The loop continues to run until Stop() is called.
 | 
				
			||||||
 | 
					// If the loop is already started it will panic.
 | 
				
			||||||
 | 
					// Use this instead of Start if you want to recover from panics that may occur while calling native Go functions from
 | 
				
			||||||
 | 
					// within setInterval and setTimeout callbacks.
 | 
				
			||||||
 | 
					func (loop *EventLoop) StartInForeground() {
 | 
				
			||||||
 | 
						loop.setRunning()
 | 
				
			||||||
 | 
						loop.run(true)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Stop the loop that was started with Start(). After this function returns there will be no more jobs executed
 | 
				
			||||||
 | 
					// by the loop. It is possible to call Start() or Run() again after this to resume the execution.
 | 
				
			||||||
 | 
					// Note, it does not cancel active timeouts (use Terminate() instead if you want this).
 | 
				
			||||||
 | 
					// It is not allowed to run Start() (or Run()) and Stop() or Terminate() concurrently.
 | 
				
			||||||
 | 
					// Calling Stop() on a non-running loop has no effect.
 | 
				
			||||||
 | 
					// It is not allowed to call Stop() from the loop, because it is synchronous and cannot complete until the loop
 | 
				
			||||||
 | 
					// is not running any jobs. Use StopNoWait() instead.
 | 
				
			||||||
 | 
					// return number of jobs remaining
 | 
				
			||||||
 | 
					func (loop *EventLoop) Stop() int {
 | 
				
			||||||
 | 
						loop.stopLock.Lock()
 | 
				
			||||||
 | 
						for loop.running {
 | 
				
			||||||
 | 
							atomic.StoreInt32(&loop.canRun, 0)
 | 
				
			||||||
 | 
							loop.wakeup()
 | 
				
			||||||
 | 
							loop.stopCond.Wait()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.stopLock.Unlock()
 | 
				
			||||||
 | 
						return int(loop.jobCount)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StopNoWait tells the loop to stop and returns immediately. Can be used inside the loop. Calling it on a
 | 
				
			||||||
 | 
					// non-running loop has no effect.
 | 
				
			||||||
 | 
					func (loop *EventLoop) StopNoWait() {
 | 
				
			||||||
 | 
						loop.stopLock.Lock()
 | 
				
			||||||
 | 
						if loop.running {
 | 
				
			||||||
 | 
							atomic.StoreInt32(&loop.canRun, 0)
 | 
				
			||||||
 | 
							loop.wakeup()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.stopLock.Unlock()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Terminate stops the loop and clears all active timeouts and intervals. After it returns there are no
 | 
				
			||||||
 | 
					// active timers or goroutines associated with the loop. Any attempt to submit a task (by using RunOnLoop(),
 | 
				
			||||||
 | 
					// SetTimeout() or SetInterval()) will not succeed.
 | 
				
			||||||
 | 
					// After being terminated the loop can be restarted again by using Start() or Run().
 | 
				
			||||||
 | 
					// This method must not be called concurrently with Stop*(), Start(), or Run().
 | 
				
			||||||
 | 
					func (loop *EventLoop) Terminate() {
 | 
				
			||||||
 | 
						loop.Stop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop.auxJobsLock.Lock()
 | 
				
			||||||
 | 
						loop.terminated = true
 | 
				
			||||||
 | 
						loop.auxJobsLock.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop.runAux()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := 0; i < len(loop.jobs); i++ {
 | 
				
			||||||
 | 
							job := loop.jobs[i]
 | 
				
			||||||
 | 
							if !job.cancelled {
 | 
				
			||||||
 | 
								job.cancelled = true
 | 
				
			||||||
 | 
								if job.cancel() {
 | 
				
			||||||
 | 
									loop.removeJob(job)
 | 
				
			||||||
 | 
									i--
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for len(loop.jobs) > 0 {
 | 
				
			||||||
 | 
							(<-loop.jobChan)()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RunOnLoop schedules to run the specified function in the context of the loop as soon as possible.
 | 
				
			||||||
 | 
					// The order of the runs is preserved (i.e. the functions will be called in the same order as calls to RunOnLoop())
 | 
				
			||||||
 | 
					// The instance of goja.Runtime that is passed to the function and any Values derived from it must not be used
 | 
				
			||||||
 | 
					// outside the function. It is safe to call inside or outside the loop.
 | 
				
			||||||
 | 
					// Returns true on success or false if the loop is terminated (see Terminate()).
 | 
				
			||||||
 | 
					func (loop *EventLoop) RunOnLoop(fn func(*goja.Runtime)) bool {
 | 
				
			||||||
 | 
						return loop.addAuxJob(func() { fn(loop.vm) })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) runAux() {
 | 
				
			||||||
 | 
						loop.auxJobsLock.Lock()
 | 
				
			||||||
 | 
						jobs := loop.auxJobs
 | 
				
			||||||
 | 
						loop.auxJobs = loop.auxJobsSpare
 | 
				
			||||||
 | 
						loop.auxJobsLock.Unlock()
 | 
				
			||||||
 | 
						for i, job := range jobs {
 | 
				
			||||||
 | 
							job()
 | 
				
			||||||
 | 
							jobs[i] = nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.auxJobsSpare = jobs[:0]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) run(inBackground bool) {
 | 
				
			||||||
 | 
						loop.runAux()
 | 
				
			||||||
 | 
						if inBackground {
 | 
				
			||||||
 | 
							loop.jobCount++
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					LOOP:
 | 
				
			||||||
 | 
						for loop.jobCount > 0 {
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case job := <-loop.jobChan:
 | 
				
			||||||
 | 
								job()
 | 
				
			||||||
 | 
							case <-loop.wakeupChan:
 | 
				
			||||||
 | 
								loop.runAux()
 | 
				
			||||||
 | 
								if atomic.LoadInt32(&loop.canRun) == 0 {
 | 
				
			||||||
 | 
									break LOOP
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if inBackground {
 | 
				
			||||||
 | 
							loop.jobCount--
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop.stopLock.Lock()
 | 
				
			||||||
 | 
						loop.running = false
 | 
				
			||||||
 | 
						loop.stopLock.Unlock()
 | 
				
			||||||
 | 
						loop.stopCond.Broadcast()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) wakeup() {
 | 
				
			||||||
 | 
						select {
 | 
				
			||||||
 | 
						case loop.wakeupChan <- struct{}{}:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) addAuxJob(fn func()) bool {
 | 
				
			||||||
 | 
						loop.auxJobsLock.Lock()
 | 
				
			||||||
 | 
						if loop.terminated {
 | 
				
			||||||
 | 
							loop.auxJobsLock.Unlock()
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.auxJobs = append(loop.auxJobs, fn)
 | 
				
			||||||
 | 
						loop.auxJobsLock.Unlock()
 | 
				
			||||||
 | 
						loop.wakeup()
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) newTimeout(f func()) *Timer {
 | 
				
			||||||
 | 
						t := &Timer{
 | 
				
			||||||
 | 
							job: job{fn: f},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						t.cancel = t.doCancel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return t
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *Timer) start(loop *EventLoop, timeout time.Duration) {
 | 
				
			||||||
 | 
						t.timer = time.AfterFunc(timeout, func() {
 | 
				
			||||||
 | 
							loop.jobChan <- func() {
 | 
				
			||||||
 | 
								loop.doTimeout(t)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) newInterval(f func()) *Interval {
 | 
				
			||||||
 | 
						i := &Interval{
 | 
				
			||||||
 | 
							job:      job{fn: f},
 | 
				
			||||||
 | 
							stopChan: make(chan struct{}),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						i.cancel = i.doCancel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return i
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (i *Interval) start(loop *EventLoop, timeout time.Duration) {
 | 
				
			||||||
 | 
						// https://nodejs.org/api/timers.html#timers_setinterval_callback_delay_args
 | 
				
			||||||
 | 
						if timeout <= 0 {
 | 
				
			||||||
 | 
							timeout = time.Millisecond
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						i.ticker = time.NewTicker(timeout)
 | 
				
			||||||
 | 
						go i.run(loop)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) addImmediate(f func()) *Immediate {
 | 
				
			||||||
 | 
						i := &Immediate{
 | 
				
			||||||
 | 
							job: job{fn: f},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.addAuxJob(func() {
 | 
				
			||||||
 | 
							loop.doImmediate(i)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return i
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) doTimeout(t *Timer) {
 | 
				
			||||||
 | 
						loop.removeJob(&t.job)
 | 
				
			||||||
 | 
						if !t.cancelled {
 | 
				
			||||||
 | 
							t.cancelled = true
 | 
				
			||||||
 | 
							loop.jobCount--
 | 
				
			||||||
 | 
							t.fn()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) doInterval(i *Interval) {
 | 
				
			||||||
 | 
						if !i.cancelled {
 | 
				
			||||||
 | 
							i.fn()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) doImmediate(i *Immediate) {
 | 
				
			||||||
 | 
						if !i.cancelled {
 | 
				
			||||||
 | 
							i.cancelled = true
 | 
				
			||||||
 | 
							loop.jobCount--
 | 
				
			||||||
 | 
							i.fn()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) clearTimeout(t *Timer) {
 | 
				
			||||||
 | 
						if t != nil && !t.cancelled {
 | 
				
			||||||
 | 
							t.cancelled = true
 | 
				
			||||||
 | 
							loop.jobCount--
 | 
				
			||||||
 | 
							if t.doCancel() {
 | 
				
			||||||
 | 
								loop.removeJob(&t.job)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) clearInterval(i *Interval) {
 | 
				
			||||||
 | 
						if i != nil && !i.cancelled {
 | 
				
			||||||
 | 
							i.cancelled = true
 | 
				
			||||||
 | 
							loop.jobCount--
 | 
				
			||||||
 | 
							i.doCancel()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) removeJob(job *job) {
 | 
				
			||||||
 | 
						idx := job.idx
 | 
				
			||||||
 | 
						if idx < 0 {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if idx < len(loop.jobs)-1 {
 | 
				
			||||||
 | 
							loop.jobs[idx] = loop.jobs[len(loop.jobs)-1]
 | 
				
			||||||
 | 
							loop.jobs[idx].idx = idx
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.jobs[len(loop.jobs)-1] = nil
 | 
				
			||||||
 | 
						loop.jobs = loop.jobs[:len(loop.jobs)-1]
 | 
				
			||||||
 | 
						job.idx = -1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (loop *EventLoop) clearImmediate(i *Immediate) {
 | 
				
			||||||
 | 
						if i != nil && !i.cancelled {
 | 
				
			||||||
 | 
							i.cancelled = true
 | 
				
			||||||
 | 
							loop.jobCount--
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (i *Interval) doCancel() bool {
 | 
				
			||||||
 | 
						close(i.stopChan)
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *Timer) doCancel() bool {
 | 
				
			||||||
 | 
						return t.timer.Stop()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (i *Interval) run(loop *EventLoop) {
 | 
				
			||||||
 | 
					L:
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case <-i.stopChan:
 | 
				
			||||||
 | 
								i.ticker.Stop()
 | 
				
			||||||
 | 
								break L
 | 
				
			||||||
 | 
							case <-i.ticker.C:
 | 
				
			||||||
 | 
								loop.jobChan <- func() {
 | 
				
			||||||
 | 
									loop.doInterval(i)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.jobChan <- func() {
 | 
				
			||||||
 | 
							loop.removeJob(&i.job)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										641
									
								
								goja_nodejs/eventloop/eventloop_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										641
									
								
								goja_nodejs/eventloop/eventloop_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,641 @@
 | 
				
			|||||||
 | 
					package eventloop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"sync/atomic"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.uber.org/goleak"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRun(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						var calledAt;
 | 
				
			||||||
 | 
						setTimeout(function() {
 | 
				
			||||||
 | 
							calledAt = now();
 | 
				
			||||||
 | 
						}, 1000);
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						prg, err := goja.Compile("main.js", SCRIPT, false)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						startTime := time.Now()
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							vm.Set("now", time.Now)
 | 
				
			||||||
 | 
							_, err = vm.RunProgram(prg)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var calledAt time.Time
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							err = vm.ExportTo(vm.Get("calledAt"), &calledAt)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if calledAt.IsZero() {
 | 
				
			||||||
 | 
							t.Fatal("Not called")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if dur := calledAt.Sub(startTime); dur < time.Second {
 | 
				
			||||||
 | 
							t.Fatal(dur)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestStart(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						var calledAt;
 | 
				
			||||||
 | 
						setTimeout(function() {
 | 
				
			||||||
 | 
							calledAt = now();
 | 
				
			||||||
 | 
						}, 1000);
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prg, err := goja.Compile("main.js", SCRIPT, false)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						startTime := time.Now()
 | 
				
			||||||
 | 
						loop.Start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop.RunOnLoop(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							vm.Set("now", time.Now)
 | 
				
			||||||
 | 
							vm.RunProgram(prg)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						time.Sleep(2 * time.Second)
 | 
				
			||||||
 | 
						if remainingJobs := loop.Stop(); remainingJobs != 0 {
 | 
				
			||||||
 | 
							t.Fatal(remainingJobs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var calledAt time.Time
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							err = vm.ExportTo(vm.Get("calledAt"), &calledAt)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if calledAt.IsZero() {
 | 
				
			||||||
 | 
							t.Fatal("Not called")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if dur := calledAt.Sub(startTime); dur < time.Second {
 | 
				
			||||||
 | 
							t.Fatal(dur)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestStartInForeground(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						var calledAt;
 | 
				
			||||||
 | 
						setTimeout(function() {
 | 
				
			||||||
 | 
							calledAt = now();
 | 
				
			||||||
 | 
						}, 1000);
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prg, err := goja.Compile("main.js", SCRIPT, false)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						startTime := time.Now()
 | 
				
			||||||
 | 
						go loop.StartInForeground()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop.RunOnLoop(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							vm.Set("now", time.Now)
 | 
				
			||||||
 | 
							vm.RunProgram(prg)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						time.Sleep(2 * time.Second)
 | 
				
			||||||
 | 
						if remainingJobs := loop.Stop(); remainingJobs != 0 {
 | 
				
			||||||
 | 
							t.Fatal(remainingJobs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var calledAt time.Time
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							err = vm.ExportTo(vm.Get("calledAt"), &calledAt)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if calledAt.IsZero() {
 | 
				
			||||||
 | 
							t.Fatal("Not called")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if dur := calledAt.Sub(startTime); dur < time.Second {
 | 
				
			||||||
 | 
							t.Fatal(dur)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestInterval(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						var count = 0;
 | 
				
			||||||
 | 
						var t = setInterval(function(times) {
 | 
				
			||||||
 | 
							console.log("tick");
 | 
				
			||||||
 | 
							if (++count > times) {
 | 
				
			||||||
 | 
								clearInterval(t);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}, 1000, 2);
 | 
				
			||||||
 | 
						console.log("Started");
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						prg, err := goja.Compile("main.js", SCRIPT, false)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							_, err = vm.RunProgram(prg)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var count int64
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							count = vm.Get("count").ToInteger()
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if count != 3 {
 | 
				
			||||||
 | 
							t.Fatal(count)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestImmediate(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						let log = [];
 | 
				
			||||||
 | 
						function cb(arg) {
 | 
				
			||||||
 | 
							log.push(arg);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var i;
 | 
				
			||||||
 | 
						var t = setImmediate(function() {
 | 
				
			||||||
 | 
							cb("tick");
 | 
				
			||||||
 | 
							setImmediate(cb, "tick 2");
 | 
				
			||||||
 | 
							i = setImmediate(cb, "should not run")
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						setImmediate(function() {
 | 
				
			||||||
 | 
							clearImmediate(i);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						cb("Started");
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						prg, err := goja.Compile("main.js", SCRIPT, false)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							_, err = vm.RunProgram(prg)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							_, err = vm.RunString(`
 | 
				
			||||||
 | 
							if (log.length != 3) {
 | 
				
			||||||
 | 
								throw new Error("Invalid log length: " + log);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (log[0] !== "Started" || log[1] !== "tick" || log[2] !== "tick 2") {
 | 
				
			||||||
 | 
								throw new Error("Invalid log: " + log);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							`)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRunNoSchedule(t *testing.T) {
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						fired := false
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) { // should not hang
 | 
				
			||||||
 | 
							fired = true
 | 
				
			||||||
 | 
							// do not schedule anything
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !fired {
 | 
				
			||||||
 | 
							t.Fatal("Not fired")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRunWithConsole(t *testing.T) {
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						console.log("Started");
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						prg, err := goja.Compile("main.js", SCRIPT, false)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							_, err = vm.RunProgram(prg)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal("Call to console.log generated an error", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop = NewEventLoop(EnableConsole(true))
 | 
				
			||||||
 | 
						prg, err = goja.Compile("main.js", SCRIPT, false)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							_, err = vm.RunProgram(prg)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal("Call to console.log generated an error", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRunNoConsole(t *testing.T) {
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						console.log("Started");
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop := NewEventLoop(EnableConsole(false))
 | 
				
			||||||
 | 
						prg, err := goja.Compile("main.js", SCRIPT, false)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							_, err = vm.RunProgram(prg)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							t.Fatal("Call to console.log did not generate an error", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestClearIntervalRace(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						console.log("calling setInterval");
 | 
				
			||||||
 | 
						var t = setInterval(function() {
 | 
				
			||||||
 | 
							console.log("tick");
 | 
				
			||||||
 | 
						}, 500);
 | 
				
			||||||
 | 
						console.log("calling sleep");
 | 
				
			||||||
 | 
						sleep(2000);
 | 
				
			||||||
 | 
						console.log("calling clearInterval");
 | 
				
			||||||
 | 
						clearInterval(t);
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						prg, err := goja.Compile("main.js", SCRIPT, false)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Should not hang
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							vm.Set("sleep", func(ms int) {
 | 
				
			||||||
 | 
								<-time.After(time.Duration(ms) * time.Millisecond)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							vm.RunProgram(prg)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNativeTimeout(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						fired := false
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						loop.SetTimeout(func(*goja.Runtime) {
 | 
				
			||||||
 | 
							fired = true
 | 
				
			||||||
 | 
						}, 1*time.Second)
 | 
				
			||||||
 | 
						loop.Run(func(*goja.Runtime) {
 | 
				
			||||||
 | 
							// do not schedule anything
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if !fired {
 | 
				
			||||||
 | 
							t.Fatal("Not fired")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNativeClearTimeout(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						fired := false
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						timer := loop.SetTimeout(func(*goja.Runtime) {
 | 
				
			||||||
 | 
							fired = true
 | 
				
			||||||
 | 
						}, 2*time.Second)
 | 
				
			||||||
 | 
						loop.SetTimeout(func(*goja.Runtime) {
 | 
				
			||||||
 | 
							loop.ClearTimeout(timer)
 | 
				
			||||||
 | 
						}, 1*time.Second)
 | 
				
			||||||
 | 
						loop.Run(func(*goja.Runtime) {
 | 
				
			||||||
 | 
							// do not schedule anything
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if fired {
 | 
				
			||||||
 | 
							t.Fatal("Cancelled timer fired!")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNativeInterval(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						count := 0
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						var i *Interval
 | 
				
			||||||
 | 
						i = loop.SetInterval(func(*goja.Runtime) {
 | 
				
			||||||
 | 
							t.Log("tick")
 | 
				
			||||||
 | 
							count++
 | 
				
			||||||
 | 
							if count > 2 {
 | 
				
			||||||
 | 
								loop.ClearInterval(i)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}, 1*time.Second)
 | 
				
			||||||
 | 
						loop.Run(func(*goja.Runtime) {
 | 
				
			||||||
 | 
							// do not schedule anything
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if count != 3 {
 | 
				
			||||||
 | 
							t.Fatal("Expected interval to fire 3 times, got", count)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNativeClearInterval(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						count := 0
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						loop.Run(func(*goja.Runtime) {
 | 
				
			||||||
 | 
							i := loop.SetInterval(func(*goja.Runtime) {
 | 
				
			||||||
 | 
								t.Log("tick")
 | 
				
			||||||
 | 
								count++
 | 
				
			||||||
 | 
							}, 500*time.Millisecond)
 | 
				
			||||||
 | 
							<-time.After(2 * time.Second)
 | 
				
			||||||
 | 
							loop.ClearInterval(i)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if count != 0 {
 | 
				
			||||||
 | 
							t.Fatal("Expected interval to fire 0 times, got", count)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSetAndClearOnStoppedLoop(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						timeout := loop.SetTimeout(func(runtime *goja.Runtime) {
 | 
				
			||||||
 | 
							panic("must not run")
 | 
				
			||||||
 | 
						}, 1*time.Millisecond)
 | 
				
			||||||
 | 
						loop.ClearTimeout(timeout)
 | 
				
			||||||
 | 
						loop.Start()
 | 
				
			||||||
 | 
						time.Sleep(10 * time.Millisecond)
 | 
				
			||||||
 | 
						loop.Terminate()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSetTimeoutConcurrent(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						loop.Start()
 | 
				
			||||||
 | 
						ch := make(chan struct{}, 1)
 | 
				
			||||||
 | 
						loop.SetTimeout(func(*goja.Runtime) {
 | 
				
			||||||
 | 
							ch <- struct{}{}
 | 
				
			||||||
 | 
						}, 100*time.Millisecond)
 | 
				
			||||||
 | 
						<-ch
 | 
				
			||||||
 | 
						loop.Stop()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestClearTimeoutConcurrent(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						loop.Start()
 | 
				
			||||||
 | 
						timer := loop.SetTimeout(func(*goja.Runtime) {
 | 
				
			||||||
 | 
						}, 100*time.Millisecond)
 | 
				
			||||||
 | 
						loop.ClearTimeout(timer)
 | 
				
			||||||
 | 
						loop.Stop()
 | 
				
			||||||
 | 
						if c := loop.jobCount; c != 0 {
 | 
				
			||||||
 | 
							t.Fatalf("jobCount: %d", c)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestClearIntervalConcurrent(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						loop.Start()
 | 
				
			||||||
 | 
						ch := make(chan struct{}, 1)
 | 
				
			||||||
 | 
						i := loop.SetInterval(func(*goja.Runtime) {
 | 
				
			||||||
 | 
							ch <- struct{}{}
 | 
				
			||||||
 | 
						}, 500*time.Millisecond)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<-ch
 | 
				
			||||||
 | 
						loop.ClearInterval(i)
 | 
				
			||||||
 | 
						loop.Stop()
 | 
				
			||||||
 | 
						if c := loop.jobCount; c != 0 {
 | 
				
			||||||
 | 
							t.Fatalf("jobCount: %d", c)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRunOnStoppedLoop(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						var failed int32
 | 
				
			||||||
 | 
						done := make(chan struct{})
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							for atomic.LoadInt32(&failed) == 0 {
 | 
				
			||||||
 | 
								loop.Start()
 | 
				
			||||||
 | 
								time.Sleep(10 * time.Millisecond)
 | 
				
			||||||
 | 
								loop.Stop()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							for atomic.LoadInt32(&failed) == 0 {
 | 
				
			||||||
 | 
								loop.RunOnLoop(func(*goja.Runtime) {
 | 
				
			||||||
 | 
									if !loop.running {
 | 
				
			||||||
 | 
										atomic.StoreInt32(&failed, 1)
 | 
				
			||||||
 | 
										close(done)
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								time.Sleep(10 * time.Millisecond)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						select {
 | 
				
			||||||
 | 
						case <-done:
 | 
				
			||||||
 | 
						case <-time.After(5 * time.Second):
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if atomic.LoadInt32(&failed) != 0 {
 | 
				
			||||||
 | 
							t.Fatal("running job on stopped loop")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPromise(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						let result;
 | 
				
			||||||
 | 
						const p = new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
							setTimeout(() => {resolve("passed")}, 500);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						p.then(value => {
 | 
				
			||||||
 | 
							result = value;
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						prg, err := goja.Compile("main.js", SCRIPT, false)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							_, err = vm.RunProgram(prg)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							result := vm.Get("result")
 | 
				
			||||||
 | 
							if !result.SameAs(vm.ToValue("passed")) {
 | 
				
			||||||
 | 
								err = fmt.Errorf("unexpected result: %v", result)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPromiseNative(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						let result;
 | 
				
			||||||
 | 
						p.then(value => {
 | 
				
			||||||
 | 
							result = value;
 | 
				
			||||||
 | 
							done();
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						prg, err := goja.Compile("main.js", SCRIPT, false)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ch := make(chan error)
 | 
				
			||||||
 | 
						loop.Start()
 | 
				
			||||||
 | 
						defer loop.Stop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop.RunOnLoop(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							vm.Set("done", func() {
 | 
				
			||||||
 | 
								ch <- nil
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							p, resolve, _ := vm.NewPromise()
 | 
				
			||||||
 | 
							vm.Set("p", p)
 | 
				
			||||||
 | 
							_, err = vm.RunProgram(prg)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ch <- err
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							go func() {
 | 
				
			||||||
 | 
								time.Sleep(500 * time.Millisecond)
 | 
				
			||||||
 | 
								loop.RunOnLoop(func(*goja.Runtime) {
 | 
				
			||||||
 | 
									resolve("passed")
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						err = <-ch
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loop.RunOnLoop(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							result := vm.Get("result")
 | 
				
			||||||
 | 
							if !result.SameAs(vm.ToValue("passed")) {
 | 
				
			||||||
 | 
								ch <- fmt.Errorf("unexpected result: %v", result)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ch <- nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						err = <-ch
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestEventLoop_StopNoWait(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						var ran int32
 | 
				
			||||||
 | 
						loop.Run(func(runtime *goja.Runtime) {
 | 
				
			||||||
 | 
							loop.SetTimeout(func(*goja.Runtime) {
 | 
				
			||||||
 | 
								atomic.StoreInt32(&ran, 1)
 | 
				
			||||||
 | 
							}, 5*time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							loop.SetTimeout(func(*goja.Runtime) {
 | 
				
			||||||
 | 
								loop.StopNoWait()
 | 
				
			||||||
 | 
							}, 500*time.Millisecond)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if atomic.LoadInt32(&ran) != 0 {
 | 
				
			||||||
 | 
							t.Fatal("ran != 0")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestEventLoop_ClearRunningTimeout(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						var called = 0;
 | 
				
			||||||
 | 
						let aTimer;
 | 
				
			||||||
 | 
						function a() {
 | 
				
			||||||
 | 
							if (++called > 5) {
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (aTimer) {
 | 
				
			||||||
 | 
								clearTimeout(aTimer);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							console.log("ok");
 | 
				
			||||||
 | 
							aTimer = setTimeout(a, 500);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						a();`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prg, err := goja.Compile("main.js", SCRIPT, false)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							_, err = vm.RunProgram(prg)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var called int64
 | 
				
			||||||
 | 
						loop.Run(func(vm *goja.Runtime) {
 | 
				
			||||||
 | 
							called = vm.Get("called").ToInteger()
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if called != 6 {
 | 
				
			||||||
 | 
							t.Fatal(called)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestEventLoop_Terminate(t *testing.T) {
 | 
				
			||||||
 | 
						defer goleak.VerifyNone(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loop := NewEventLoop()
 | 
				
			||||||
 | 
						loop.Start()
 | 
				
			||||||
 | 
						interval := loop.SetInterval(func(vm *goja.Runtime) {}, 10*time.Millisecond)
 | 
				
			||||||
 | 
						time.Sleep(500 * time.Millisecond)
 | 
				
			||||||
 | 
						loop.ClearInterval(interval)
 | 
				
			||||||
 | 
						loop.Terminate()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if loop.SetTimeout(func(*goja.Runtime) {}, time.Millisecond) != nil {
 | 
				
			||||||
 | 
							t.Fatal("was able to SetTimeout()")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if loop.SetInterval(func(*goja.Runtime) {}, time.Millisecond) != nil {
 | 
				
			||||||
 | 
							t.Fatal("was able to SetInterval()")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if loop.RunOnLoop(func(*goja.Runtime) {}) {
 | 
				
			||||||
 | 
							t.Fatal("was able to RunOnLoop()")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ch := make(chan struct{})
 | 
				
			||||||
 | 
						loop.Start()
 | 
				
			||||||
 | 
						if !loop.RunOnLoop(func(runtime *goja.Runtime) {
 | 
				
			||||||
 | 
							close(ch)
 | 
				
			||||||
 | 
						}) {
 | 
				
			||||||
 | 
							t.Fatal("RunOnLoop() has failed after restart")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						<-ch
 | 
				
			||||||
 | 
						loop.Terminate()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										37
									
								
								goja_nodejs/process/module.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								goja_nodejs/process/module.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					package process
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ModuleName = "process"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Process struct {
 | 
				
			||||||
 | 
						env map[string]string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Require(runtime *goja.Runtime, module *goja.Object) {
 | 
				
			||||||
 | 
						p := &Process{
 | 
				
			||||||
 | 
							env: make(map[string]string),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, e := range os.Environ() {
 | 
				
			||||||
 | 
							envKeyValue := strings.SplitN(e, "=", 2)
 | 
				
			||||||
 | 
							p.env[envKeyValue[0]] = envKeyValue[1]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						o := module.Get("exports").(*goja.Object)
 | 
				
			||||||
 | 
						o.Set("env", p.env)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Enable(runtime *goja.Runtime) {
 | 
				
			||||||
 | 
						runtime.Set("process", require.Require(runtime, ModuleName))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						require.RegisterCoreModule(ModuleName, Require)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										68
									
								
								goja_nodejs/process/module_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								goja_nodejs/process/module_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					package process
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestProcessEnvStructure(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						new(require.Registry).Enable(vm)
 | 
				
			||||||
 | 
						Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c := vm.Get("process"); c == nil {
 | 
				
			||||||
 | 
							t.Fatal("process not found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c, err := vm.RunString("process.env"); c == nil || err != nil {
 | 
				
			||||||
 | 
							t.Fatal("error accessing process.env")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestProcessEnvValuesArtificial(t *testing.T) {
 | 
				
			||||||
 | 
						os.Setenv("GOJA_IS_AWESOME", "true")
 | 
				
			||||||
 | 
						defer os.Unsetenv("GOJA_IS_AWESOME")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						new(require.Registry).Enable(vm)
 | 
				
			||||||
 | 
						Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						jsRes, err := vm.RunString("process.env['GOJA_IS_AWESOME']")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Error executing: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if jsRes.String() != "true" {
 | 
				
			||||||
 | 
							t.Fatalf("Error executing: got %s but expected %s", jsRes, "true")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestProcessEnvValuesBrackets(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						new(require.Registry).Enable(vm)
 | 
				
			||||||
 | 
						Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, e := range os.Environ() {
 | 
				
			||||||
 | 
							envKeyValue := strings.SplitN(e, "=", 2)
 | 
				
			||||||
 | 
							jsExpr := fmt.Sprintf("process.env['%s']", envKeyValue[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							jsRes, err := vm.RunString(jsExpr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Fatalf("Error executing %s: %s", jsExpr, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if jsRes.String() != envKeyValue[1] {
 | 
				
			||||||
 | 
								t.Fatalf("Error executing %s: got %s but expected %s", jsExpr, jsRes, envKeyValue[1])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										246
									
								
								goja_nodejs/require/module.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								goja_nodejs/require/module.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,246 @@
 | 
				
			|||||||
 | 
					package require
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"io/fs"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"text/template"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						js "apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja/parser"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ModuleLoader func(*js.Runtime, *js.Object)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SourceLoader represents a function that returns a file data at a given path.
 | 
				
			||||||
 | 
					// The function should return ModuleFileDoesNotExistError if the file either doesn't exist or is a directory.
 | 
				
			||||||
 | 
					// This error will be ignored by the resolver and the search will continue. Any other errors will be propagated.
 | 
				
			||||||
 | 
					type SourceLoader func(path string) ([]byte, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						InvalidModuleError          = errors.New("Invalid module")
 | 
				
			||||||
 | 
						IllegalModuleNameError      = errors.New("Illegal module name")
 | 
				
			||||||
 | 
						NoSuchBuiltInModuleError    = errors.New("No such built-in module")
 | 
				
			||||||
 | 
						ModuleFileDoesNotExistError = errors.New("module file does not exist")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var native, builtin map[string]ModuleLoader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Registry contains a cache of compiled modules which can be used by multiple Runtimes
 | 
				
			||||||
 | 
					type Registry struct {
 | 
				
			||||||
 | 
						sync.Mutex
 | 
				
			||||||
 | 
						native   map[string]ModuleLoader
 | 
				
			||||||
 | 
						compiled map[string]*js.Program
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						srcLoader     SourceLoader
 | 
				
			||||||
 | 
						globalFolders []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type RequireModule struct {
 | 
				
			||||||
 | 
						r           *Registry
 | 
				
			||||||
 | 
						runtime     *js.Runtime
 | 
				
			||||||
 | 
						modules     map[string]*js.Object
 | 
				
			||||||
 | 
						nodeModules map[string]*js.Object
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewRegistry(opts ...Option) *Registry {
 | 
				
			||||||
 | 
						r := &Registry{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, opt := range opts {
 | 
				
			||||||
 | 
							opt(r)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return r
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewRegistryWithLoader(srcLoader SourceLoader) *Registry {
 | 
				
			||||||
 | 
						return NewRegistry(WithLoader(srcLoader))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Option func(*Registry)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithLoader sets a function which will be called by the require() function in order to get a source code for a
 | 
				
			||||||
 | 
					// module at the given path. The same function will be used to get external source maps.
 | 
				
			||||||
 | 
					// Note, this only affects the modules loaded by the require() function. If you need to use it as a source map
 | 
				
			||||||
 | 
					// loader for code parsed in a different way (such as runtime.RunString() or eval()), use (*Runtime).SetParserOptions()
 | 
				
			||||||
 | 
					func WithLoader(srcLoader SourceLoader) Option {
 | 
				
			||||||
 | 
						return func(r *Registry) {
 | 
				
			||||||
 | 
							r.srcLoader = srcLoader
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithGlobalFolders appends the given paths to the registry's list of
 | 
				
			||||||
 | 
					// global folders to search if the requested module is not found
 | 
				
			||||||
 | 
					// elsewhere.  By default, a registry's global folders list is empty.
 | 
				
			||||||
 | 
					// In the reference Node.js implementation, the default global folders
 | 
				
			||||||
 | 
					// list is $NODE_PATH, $HOME/.node_modules, $HOME/.node_libraries and
 | 
				
			||||||
 | 
					// $PREFIX/lib/node, see
 | 
				
			||||||
 | 
					// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders.
 | 
				
			||||||
 | 
					func WithGlobalFolders(globalFolders ...string) Option {
 | 
				
			||||||
 | 
						return func(r *Registry) {
 | 
				
			||||||
 | 
							r.globalFolders = globalFolders
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Enable adds the require() function to the specified runtime.
 | 
				
			||||||
 | 
					func (r *Registry) Enable(runtime *js.Runtime) *RequireModule {
 | 
				
			||||||
 | 
						rrt := &RequireModule{
 | 
				
			||||||
 | 
							r:           r,
 | 
				
			||||||
 | 
							runtime:     runtime,
 | 
				
			||||||
 | 
							modules:     make(map[string]*js.Object),
 | 
				
			||||||
 | 
							nodeModules: make(map[string]*js.Object),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						runtime.Set("require", rrt.require)
 | 
				
			||||||
 | 
						return rrt
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Registry) RegisterNativeModule(name string, loader ModuleLoader) {
 | 
				
			||||||
 | 
						r.Lock()
 | 
				
			||||||
 | 
						defer r.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if r.native == nil {
 | 
				
			||||||
 | 
							r.native = make(map[string]ModuleLoader)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						name = filepathClean(name)
 | 
				
			||||||
 | 
						r.native[name] = loader
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DefaultSourceLoader is used if none was set (see WithLoader()). It simply loads files from the host's filesystem.
 | 
				
			||||||
 | 
					func DefaultSourceLoader(filename string) ([]byte, error) {
 | 
				
			||||||
 | 
						fp := filepath.FromSlash(filename)
 | 
				
			||||||
 | 
						f, err := os.Open(fp)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, fs.ErrNotExist) {
 | 
				
			||||||
 | 
								err = ModuleFileDoesNotExistError
 | 
				
			||||||
 | 
							} else if runtime.GOOS == "windows" {
 | 
				
			||||||
 | 
								if errors.Is(err, syscall.Errno(0x7b)) { // ERROR_INVALID_NAME, The filename, directory name, or volume label syntax is incorrect.
 | 
				
			||||||
 | 
									err = ModuleFileDoesNotExistError
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer f.Close()
 | 
				
			||||||
 | 
						// On some systems (e.g. plan9 and FreeBSD) it is possible to use the standard read() call on directories
 | 
				
			||||||
 | 
						// which means we cannot rely on read() returning an error, we have to do stat() instead.
 | 
				
			||||||
 | 
						if fi, err := f.Stat(); err == nil {
 | 
				
			||||||
 | 
							if fi.IsDir() {
 | 
				
			||||||
 | 
								return nil, ModuleFileDoesNotExistError
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return io.ReadAll(f)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Registry) getSource(p string) ([]byte, error) {
 | 
				
			||||||
 | 
						srcLoader := r.srcLoader
 | 
				
			||||||
 | 
						if srcLoader == nil {
 | 
				
			||||||
 | 
							srcLoader = DefaultSourceLoader
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return srcLoader(p)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Registry) getCompiledSource(p string) (*js.Program, error) {
 | 
				
			||||||
 | 
						r.Lock()
 | 
				
			||||||
 | 
						defer r.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prg := r.compiled[p]
 | 
				
			||||||
 | 
						if prg == nil {
 | 
				
			||||||
 | 
							buf, err := r.getSource(p)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							s := string(buf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if path.Ext(p) == ".json" {
 | 
				
			||||||
 | 
								s = "module.exports = JSON.parse('" + template.JSEscapeString(s) + "')"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							source := "(function(exports, require, module) {" + s + "\n})"
 | 
				
			||||||
 | 
							parsed, err := js.Parse(p, source, parser.WithSourceMapLoader(r.srcLoader))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							prg, err = js.CompileAST(parsed, false)
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								if r.compiled == nil {
 | 
				
			||||||
 | 
									r.compiled = make(map[string]*js.Program)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								r.compiled[p] = prg
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return prg, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return prg, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequireModule) require(call js.FunctionCall) js.Value {
 | 
				
			||||||
 | 
						ret, err := r.Require(call.Argument(0).String())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if _, ok := err.(*js.Exception); !ok {
 | 
				
			||||||
 | 
								panic(r.runtime.NewGoError(err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ret
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func filepathClean(p string) string {
 | 
				
			||||||
 | 
						return path.Clean(p)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Require can be used to import modules from Go source (similar to JS require() function).
 | 
				
			||||||
 | 
					func (r *RequireModule) Require(p string) (ret js.Value, err error) {
 | 
				
			||||||
 | 
						module, err := r.resolve(p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ret = module.Get("exports")
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Require(runtime *js.Runtime, name string) js.Value {
 | 
				
			||||||
 | 
						if r, ok := js.AssertFunction(runtime.Get("require")); ok {
 | 
				
			||||||
 | 
							mod, err := r(js.Undefined(), runtime.ToValue(name))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return mod
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						panic(runtime.NewTypeError("Please enable require for this runtime using new(require.Registry).Enable(runtime)"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RegisterNativeModule registers a module that isn't loaded through a SourceLoader, but rather through
 | 
				
			||||||
 | 
					// a provided ModuleLoader. Typically, this will be a module implemented in Go (although theoretically
 | 
				
			||||||
 | 
					// it can be anything, depending on the ModuleLoader implementation).
 | 
				
			||||||
 | 
					// Such modules take precedence over modules loaded through a SourceLoader, i.e. if a module name resolves as
 | 
				
			||||||
 | 
					// native, the native module is loaded, and the SourceLoader is not consulted.
 | 
				
			||||||
 | 
					// The binding is global and affects all instances of Registry.
 | 
				
			||||||
 | 
					// It should be called from a package init() function as it may not be used concurrently with require() calls.
 | 
				
			||||||
 | 
					// For registry-specific bindings see Registry.RegisterNativeModule.
 | 
				
			||||||
 | 
					func RegisterNativeModule(name string, loader ModuleLoader) {
 | 
				
			||||||
 | 
						if native == nil {
 | 
				
			||||||
 | 
							native = make(map[string]ModuleLoader)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						name = filepathClean(name)
 | 
				
			||||||
 | 
						native[name] = loader
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RegisterCoreModule registers a nodejs core module. If the name does not start with "node:", the module
 | 
				
			||||||
 | 
					// will also be loadable as "node:<name>". Hence, for "builtin" modules (such as buffer, console, etc.)
 | 
				
			||||||
 | 
					// the name should not include the "node:" prefix, but for prefix-only core modules (such as "node:test")
 | 
				
			||||||
 | 
					// it should include the prefix.
 | 
				
			||||||
 | 
					func RegisterCoreModule(name string, loader ModuleLoader) {
 | 
				
			||||||
 | 
						if builtin == nil {
 | 
				
			||||||
 | 
							builtin = make(map[string]ModuleLoader)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						name = filepathClean(name)
 | 
				
			||||||
 | 
						builtin[name] = loader
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										534
									
								
								goja_nodejs/require/module_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										534
									
								
								goja_nodejs/require/module_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,534 @@
 | 
				
			|||||||
 | 
					package require
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						js "apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func mapFileSystemSourceLoader(files map[string]string) SourceLoader {
 | 
				
			||||||
 | 
						return func(path string) ([]byte, error) {
 | 
				
			||||||
 | 
							s, ok := files[path]
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								return nil, ModuleFileDoesNotExistError
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return []byte(s), nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRequireNativeModule(t *testing.T) {
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						var m = require("test/m");
 | 
				
			||||||
 | 
						m.test();
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vm := js.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						registry := new(Registry)
 | 
				
			||||||
 | 
						registry.Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RegisterNativeModule("test/m", func(runtime *js.Runtime, module *js.Object) {
 | 
				
			||||||
 | 
							o := module.Get("exports").(*js.Object)
 | 
				
			||||||
 | 
							o.Set("test", func(call js.FunctionCall) js.Value {
 | 
				
			||||||
 | 
								return runtime.ToValue("passed")
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v, err := vm.RunString(SCRIPT)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !v.StrictEquals(vm.ToValue("passed")) {
 | 
				
			||||||
 | 
							t.Fatalf("Unexpected result: %v", v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRegisterCoreModule(t *testing.T) {
 | 
				
			||||||
 | 
						vm := js.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						registry := new(Registry)
 | 
				
			||||||
 | 
						registry.Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RegisterCoreModule("coremod", func(runtime *js.Runtime, module *js.Object) {
 | 
				
			||||||
 | 
							o := module.Get("exports").(*js.Object)
 | 
				
			||||||
 | 
							o.Set("test", func(call js.FunctionCall) js.Value {
 | 
				
			||||||
 | 
								return runtime.ToValue("passed")
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RegisterCoreModule("coremod1", func(runtime *js.Runtime, module *js.Object) {
 | 
				
			||||||
 | 
							o := module.Get("exports").(*js.Object)
 | 
				
			||||||
 | 
							o.Set("test", func(call js.FunctionCall) js.Value {
 | 
				
			||||||
 | 
								return runtime.ToValue("passed1")
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RegisterCoreModule("node:test1", func(runtime *js.Runtime, module *js.Object) {
 | 
				
			||||||
 | 
							o := module.Get("exports").(*js.Object)
 | 
				
			||||||
 | 
							o.Set("test", func(call js.FunctionCall) js.Value {
 | 
				
			||||||
 | 
								return runtime.ToValue("test1 passed")
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						registry.RegisterNativeModule("bob", func(runtime *js.Runtime, module *js.Object) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := vm.RunString(`
 | 
				
			||||||
 | 
						const m1 = require("coremod");
 | 
				
			||||||
 | 
						const m2 = require("node:coremod");
 | 
				
			||||||
 | 
						if (m1 !== m2) {
 | 
				
			||||||
 | 
							throw new Error("Modules are not equal");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (m1.test() !== "passed") {
 | 
				
			||||||
 | 
							throw new Error("m1.test() has failed");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const m3 = require("node:coremod1");
 | 
				
			||||||
 | 
						const m4 = require("coremod1");
 | 
				
			||||||
 | 
						if (m3 !== m4) {
 | 
				
			||||||
 | 
							throw new Error("Modules are not equal (1)");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (m3.test() !== "passed1") {
 | 
				
			||||||
 | 
							throw new Error("m3.test() has failed");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							require("node:bob");
 | 
				
			||||||
 | 
						} catch (e) {
 | 
				
			||||||
 | 
							if (!e.message.includes("No such built-in module")) {
 | 
				
			||||||
 | 
								throw e;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						require("bob");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							require("test1");
 | 
				
			||||||
 | 
							throw new Error("Expected exception");
 | 
				
			||||||
 | 
						} catch (e) {
 | 
				
			||||||
 | 
							if (!e.message.includes("Invalid module")) {
 | 
				
			||||||
 | 
								throw e;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (require("node:test1").test() !== "test1 passed") {
 | 
				
			||||||
 | 
							throw new Error("test1.test() has failed");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRequireRegistryNativeModule(t *testing.T) {
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						var log = require("test/log");
 | 
				
			||||||
 | 
						log.print('passed');
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logWithOutput := func(w io.Writer, prefix string) ModuleLoader {
 | 
				
			||||||
 | 
							return func(vm *js.Runtime, module *js.Object) {
 | 
				
			||||||
 | 
								o := module.Get("exports").(*js.Object)
 | 
				
			||||||
 | 
								o.Set("print", func(call js.FunctionCall) js.Value {
 | 
				
			||||||
 | 
									fmt.Fprint(w, prefix, call.Argument(0).String())
 | 
				
			||||||
 | 
									return js.Undefined()
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vm1 := js.New()
 | 
				
			||||||
 | 
						buf1 := &bytes.Buffer{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						registry1 := new(Registry)
 | 
				
			||||||
 | 
						registry1.Enable(vm1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						registry1.RegisterNativeModule("test/log", logWithOutput(buf1, "vm1 "))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vm2 := js.New()
 | 
				
			||||||
 | 
						buf2 := &bytes.Buffer{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						registry2 := new(Registry)
 | 
				
			||||||
 | 
						registry2.Enable(vm2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						registry2.RegisterNativeModule("test/log", logWithOutput(buf2, "vm2 "))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := vm1.RunString(SCRIPT)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s := buf1.String()
 | 
				
			||||||
 | 
						if s != "vm1 passed" {
 | 
				
			||||||
 | 
							t.Fatalf("vm1: Unexpected result: %q", s)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = vm2.RunString(SCRIPT)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s = buf2.String()
 | 
				
			||||||
 | 
						if s != "vm2 passed" {
 | 
				
			||||||
 | 
							t.Fatalf("vm2: Unexpected result: %q", s)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRequire(t *testing.T) {
 | 
				
			||||||
 | 
						absPath, err := filepath.Abs("./testdata/m.js")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isWindows := runtime.GOOS == "windows"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							path string
 | 
				
			||||||
 | 
							ok   bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"./testdata/m.js",
 | 
				
			||||||
 | 
								true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"../require/testdata/m.js",
 | 
				
			||||||
 | 
								true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								absPath,
 | 
				
			||||||
 | 
								true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								`.\testdata\m.js`,
 | 
				
			||||||
 | 
								isWindows,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								`..\require\testdata\m.js`,
 | 
				
			||||||
 | 
								isWindows,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						var m = require(testPath);
 | 
				
			||||||
 | 
						m.test();
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, test := range tests {
 | 
				
			||||||
 | 
							t.Run(test.path, func(t *testing.T) {
 | 
				
			||||||
 | 
								vm := js.New()
 | 
				
			||||||
 | 
								vm.Set("testPath", test.path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								registry := new(Registry)
 | 
				
			||||||
 | 
								registry.Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								v, err := vm.RunString(SCRIPT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ok := err == nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if ok != test.ok {
 | 
				
			||||||
 | 
									t.Fatalf("Expected ok to be %v, got %v (%v)", test.ok, ok, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if !ok {
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if !v.StrictEquals(vm.ToValue("passed")) {
 | 
				
			||||||
 | 
									t.Fatalf("Unexpected result: %v", v)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSourceLoader(t *testing.T) {
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						var m = require("m.js");
 | 
				
			||||||
 | 
						m.test();
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const MODULE = `
 | 
				
			||||||
 | 
						function test() {
 | 
				
			||||||
 | 
							return "passed1";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exports.test = test;
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vm := js.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						registry := NewRegistry(WithGlobalFolders("."), WithLoader(func(name string) ([]byte, error) {
 | 
				
			||||||
 | 
							if name == "m.js" {
 | 
				
			||||||
 | 
								return []byte(MODULE), nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil, errors.New("Module does not exist")
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
						registry.Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v, err := vm.RunString(SCRIPT)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !v.StrictEquals(vm.ToValue("passed1")) {
 | 
				
			||||||
 | 
							t.Fatalf("Unexpected result: %v", v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestStrictModule(t *testing.T) {
 | 
				
			||||||
 | 
						const SCRIPT = `
 | 
				
			||||||
 | 
						var m = require("m.js");
 | 
				
			||||||
 | 
						m.test();
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const MODULE = `
 | 
				
			||||||
 | 
						"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function test() {
 | 
				
			||||||
 | 
							var a = "passed1";
 | 
				
			||||||
 | 
							eval("var a = 'not passed'");
 | 
				
			||||||
 | 
							return a;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exports.test = test;
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vm := js.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						registry := NewRegistry(WithGlobalFolders("."), WithLoader(func(name string) ([]byte, error) {
 | 
				
			||||||
 | 
							if name == "m.js" {
 | 
				
			||||||
 | 
								return []byte(MODULE), nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil, errors.New("Module does not exist")
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
						registry.Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v, err := vm.RunString(SCRIPT)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !v.StrictEquals(vm.ToValue("passed1")) {
 | 
				
			||||||
 | 
							t.Fatalf("Unexpected result: %v", v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestResolve(t *testing.T) {
 | 
				
			||||||
 | 
						testRequire := func(src, fpath string, globalFolders []string, fs map[string]string) (*js.Runtime, js.Value, error) {
 | 
				
			||||||
 | 
							vm := js.New()
 | 
				
			||||||
 | 
							r := NewRegistry(WithGlobalFolders(globalFolders...), WithLoader(mapFileSystemSourceLoader(fs)))
 | 
				
			||||||
 | 
							r.Enable(vm)
 | 
				
			||||||
 | 
							t.Logf("Require(%s)", fpath)
 | 
				
			||||||
 | 
							ret, err := vm.RunScript(path.Join(src, "test.js"), fmt.Sprintf("require('%s')", fpath))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return vm, ret, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						globalFolders := []string{
 | 
				
			||||||
 | 
							"/usr/lib/node_modules",
 | 
				
			||||||
 | 
							"/home/src/.node_modules",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs := map[string]string{
 | 
				
			||||||
 | 
							"/home/src/app/app.js":                   `exports.name = "app"`,
 | 
				
			||||||
 | 
							"/home/src/app2/app2.json":               `{"name": "app2"}`,
 | 
				
			||||||
 | 
							"/home/src/app3/index.js":                `exports.name = "app3"`,
 | 
				
			||||||
 | 
							"/home/src/app4/index.json":              `{"name": "app4"}`,
 | 
				
			||||||
 | 
							"/home/src/app5/package.json":            `{"main": "app5.js"}`,
 | 
				
			||||||
 | 
							"/home/src/app5/app5.js":                 `exports.name = "app5"`,
 | 
				
			||||||
 | 
							"/home/src/app6/package.json":            `{"main": "."}`,
 | 
				
			||||||
 | 
							"/home/src/app6/index.js":                `exports.name = "app6"`,
 | 
				
			||||||
 | 
							"/home/src/app7/package.json":            `{"main": "./a/b/c/file.js"}`,
 | 
				
			||||||
 | 
							"/home/src/app7/a/b/c/file.js":           `exports.name = "app7"`,
 | 
				
			||||||
 | 
							"/usr/lib/node_modules/app8":             `exports.name = "app8"`,
 | 
				
			||||||
 | 
							"/home/src/app9/app9.js":                 `exports.name = require('./a/file.js').name`,
 | 
				
			||||||
 | 
							"/home/src/app9/a/file.js":               `exports.name = require('./b/file.js').name`,
 | 
				
			||||||
 | 
							"/home/src/app9/a/b/file.js":             `exports.name = require('./c/file.js').name`,
 | 
				
			||||||
 | 
							"/home/src/app9/a/b/c/file.js":           `exports.name = "app9"`,
 | 
				
			||||||
 | 
							"/home/src/.node_modules/app10":          `exports.name = "app10"`,
 | 
				
			||||||
 | 
							"/home/src/app11/app11.js":               `exports.name = require('d/file.js').name`,
 | 
				
			||||||
 | 
							"/home/src/app11/a/b/c/app11.js":         `exports.name = require('d/file.js').name`,
 | 
				
			||||||
 | 
							"/home/src/app11/node_modules/d/file.js": `exports.name = "app11"`,
 | 
				
			||||||
 | 
							"/app12.js":                              `exports.name = require('a/file.js').name`,
 | 
				
			||||||
 | 
							"/node_modules/a/file.js":                `exports.name = "app12"`,
 | 
				
			||||||
 | 
							"/app13/app13.js":                        `exports.name = require('b/file.js').name`,
 | 
				
			||||||
 | 
							"/node_modules/b/file.js":                `exports.name = "app13"`,
 | 
				
			||||||
 | 
							"node_modules/app14/index.js":            `exports.name = "app14"`,
 | 
				
			||||||
 | 
							"../node_modules/app15/index.js":         `exports.name = "app15"`,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, tc := range []struct {
 | 
				
			||||||
 | 
							src   string
 | 
				
			||||||
 | 
							path  string
 | 
				
			||||||
 | 
							ok    bool
 | 
				
			||||||
 | 
							field string
 | 
				
			||||||
 | 
							value string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{"/home/src", "./app/app", true, "name", "app"},
 | 
				
			||||||
 | 
							{"/home/src", "./app/app.js", true, "name", "app"},
 | 
				
			||||||
 | 
							{"/home/src", "./app/bad.js", false, "", ""},
 | 
				
			||||||
 | 
							{"/home/src", "./app2/app2", true, "name", "app2"},
 | 
				
			||||||
 | 
							{"/home/src", "./app2/app2.json", true, "name", "app2"},
 | 
				
			||||||
 | 
							{"/home/src", "./app/bad.json", false, "", ""},
 | 
				
			||||||
 | 
							{"/home/src", "./app3", true, "name", "app3"},
 | 
				
			||||||
 | 
							{"/home/src", "./appbad", false, "", ""},
 | 
				
			||||||
 | 
							{"/home/src", "./app4", true, "name", "app4"},
 | 
				
			||||||
 | 
							{"/home/src", "./appbad", false, "", ""},
 | 
				
			||||||
 | 
							{"/home/src", "./app5", true, "name", "app5"},
 | 
				
			||||||
 | 
							{"/home/src", "./app6", true, "name", "app6"},
 | 
				
			||||||
 | 
							{"/home/src", "./app7", true, "name", "app7"},
 | 
				
			||||||
 | 
							{"/home/src", "app8", true, "name", "app8"},
 | 
				
			||||||
 | 
							{"/home/src", "./app9/app9", true, "name", "app9"},
 | 
				
			||||||
 | 
							{"/home/src", "app10", true, "name", "app10"},
 | 
				
			||||||
 | 
							{"/home/src", "./app11/app11.js", true, "name", "app11"},
 | 
				
			||||||
 | 
							{"/home/src", "./app11/a/b/c/app11.js", true, "name", "app11"},
 | 
				
			||||||
 | 
							{"/", "./app12", true, "name", "app12"},
 | 
				
			||||||
 | 
							{"/", "./app13/app13", true, "name", "app13"},
 | 
				
			||||||
 | 
							{".", "app14", true, "name", "app14"},
 | 
				
			||||||
 | 
							{"..", "nonexistent", false, "", ""},
 | 
				
			||||||
 | 
						} {
 | 
				
			||||||
 | 
							vm, mod, err := testRequire(tc.src, tc.path, globalFolders, fs)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if tc.ok {
 | 
				
			||||||
 | 
									t.Errorf("%d: require() failed: %v", i, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if !tc.ok {
 | 
				
			||||||
 | 
									t.Errorf("%d: expected to fail, but did not", i)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							f := mod.ToObject(vm).Get(tc.field)
 | 
				
			||||||
 | 
							if f == nil {
 | 
				
			||||||
 | 
								t.Errorf("%v: field %q not found", i, tc.field)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							value := f.String()
 | 
				
			||||||
 | 
							if value != tc.value {
 | 
				
			||||||
 | 
								t.Errorf("%v: got %q expected %q", i, value, tc.value)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRequireCycle(t *testing.T) {
 | 
				
			||||||
 | 
						vm := js.New()
 | 
				
			||||||
 | 
						r := NewRegistry(WithLoader(mapFileSystemSourceLoader(map[string]string{
 | 
				
			||||||
 | 
							"a.js": `var b = require('./b.js'); exports.done = true;`,
 | 
				
			||||||
 | 
							"b.js": `var a = require('./a.js'); exports.done = true;`,
 | 
				
			||||||
 | 
						})))
 | 
				
			||||||
 | 
						r.Enable(vm)
 | 
				
			||||||
 | 
						res, err := vm.RunString(`
 | 
				
			||||||
 | 
						var a = require('./a.js');
 | 
				
			||||||
 | 
						var b = require('./b.js');
 | 
				
			||||||
 | 
						a.done && b.done;
 | 
				
			||||||
 | 
						`)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if v := res.Export(); v != true {
 | 
				
			||||||
 | 
							t.Fatalf("Unexpected result: %v", v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestErrorPropagation(t *testing.T) {
 | 
				
			||||||
 | 
						vm := js.New()
 | 
				
			||||||
 | 
						r := NewRegistry(WithLoader(mapFileSystemSourceLoader(map[string]string{
 | 
				
			||||||
 | 
							"m.js": `throw 'test passed';`,
 | 
				
			||||||
 | 
						})))
 | 
				
			||||||
 | 
						rr := r.Enable(vm)
 | 
				
			||||||
 | 
						_, err := rr.Require("./m")
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							t.Fatal("Expected an error")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ex, ok := err.(*js.Exception); ok {
 | 
				
			||||||
 | 
							if !ex.Value().StrictEquals(vm.ToValue("test passed")) {
 | 
				
			||||||
 | 
								t.Fatalf("Unexpected Exception: %v", ex)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSourceMapLoader(t *testing.T) {
 | 
				
			||||||
 | 
						vm := js.New()
 | 
				
			||||||
 | 
						r := NewRegistry(WithLoader(func(p string) ([]byte, error) {
 | 
				
			||||||
 | 
							switch p {
 | 
				
			||||||
 | 
							case "dir/m.js":
 | 
				
			||||||
 | 
								return []byte(`throw 'test passed';
 | 
				
			||||||
 | 
					//# sourceMappingURL=m.js.map`), nil
 | 
				
			||||||
 | 
							case "dir/m.js.map":
 | 
				
			||||||
 | 
								return []byte(`{"version":3,"file":"m.js","sourceRoot":"","sources":["m.ts"],"names":[],"mappings":";AAAA"}
 | 
				
			||||||
 | 
					`), nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil, ModuleFileDoesNotExistError
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rr := r.Enable(vm)
 | 
				
			||||||
 | 
						_, err := rr.Require("./dir/m")
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							t.Fatal("Expected an error")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ex, ok := err.(*js.Exception); ok {
 | 
				
			||||||
 | 
							if !ex.Value().StrictEquals(vm.ToValue("test passed")) {
 | 
				
			||||||
 | 
								t.Fatalf("Unexpected Exception: %v", ex)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testsetup() (string, func(), error) {
 | 
				
			||||||
 | 
						name, err := os.MkdirTemp("", "goja-nodejs-require-test")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return name, func() {
 | 
				
			||||||
 | 
							os.RemoveAll(name)
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDefaultModuleLoader(t *testing.T) {
 | 
				
			||||||
 | 
						workdir, teardown, err := testsetup()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer teardown()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = os.Chdir(workdir)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = os.Mkdir("module", 0755)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = os.WriteFile("module/index.js", []byte(`throw 'test passed';`), 0644)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						vm := js.New()
 | 
				
			||||||
 | 
						r := NewRegistry()
 | 
				
			||||||
 | 
						rr := r.Enable(vm)
 | 
				
			||||||
 | 
						_, err = rr.Require("./module")
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							t.Fatal("Expected an error")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ex, ok := err.(*js.Exception); ok {
 | 
				
			||||||
 | 
							if !ex.Value().StrictEquals(vm.ToValue("test passed")) {
 | 
				
			||||||
 | 
								t.Fatalf("Unexpected Exception: %v", ex)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										276
									
								
								goja_nodejs/require/resolve.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										276
									
								
								goja_nodejs/require/resolve.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,276 @@
 | 
				
			|||||||
 | 
					package require
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						js "apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const NodePrefix = "node:"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NodeJS module search algorithm described by
 | 
				
			||||||
 | 
					// https://nodejs.org/api/modules.html#modules_all_together
 | 
				
			||||||
 | 
					func (r *RequireModule) resolve(modpath string) (module *js.Object, err error) {
 | 
				
			||||||
 | 
						origPath, modpath := modpath, filepathClean(modpath)
 | 
				
			||||||
 | 
						if modpath == "" {
 | 
				
			||||||
 | 
							return nil, IllegalModuleNameError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var start string
 | 
				
			||||||
 | 
						err = nil
 | 
				
			||||||
 | 
						if path.IsAbs(origPath) {
 | 
				
			||||||
 | 
							start = "/"
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							start = r.getCurrentModulePath()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p := path.Join(start, modpath)
 | 
				
			||||||
 | 
						if isFileOrDirectoryPath(origPath) {
 | 
				
			||||||
 | 
							if module = r.modules[p]; module != nil {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							module, err = r.loadAsFileOrDirectory(p)
 | 
				
			||||||
 | 
							if err == nil && module != nil {
 | 
				
			||||||
 | 
								r.modules[p] = module
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							module, err = r.loadNative(origPath)
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if err == InvalidModuleError {
 | 
				
			||||||
 | 
									err = nil
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if module = r.nodeModules[p]; module != nil {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							module, err = r.loadNodeModules(modpath, start)
 | 
				
			||||||
 | 
							if err == nil && module != nil {
 | 
				
			||||||
 | 
								r.nodeModules[p] = module
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if module == nil && err == nil {
 | 
				
			||||||
 | 
							err = InvalidModuleError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequireModule) loadNative(path string) (*js.Object, error) {
 | 
				
			||||||
 | 
						module := r.modules[path]
 | 
				
			||||||
 | 
						if module != nil {
 | 
				
			||||||
 | 
							return module, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var ldr ModuleLoader
 | 
				
			||||||
 | 
						if ldr = r.r.native[path]; ldr == nil {
 | 
				
			||||||
 | 
							ldr = native[path]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var isBuiltIn, withPrefix bool
 | 
				
			||||||
 | 
						if ldr == nil {
 | 
				
			||||||
 | 
							ldr = builtin[path]
 | 
				
			||||||
 | 
							if ldr == nil && strings.HasPrefix(path, NodePrefix) {
 | 
				
			||||||
 | 
								ldr = builtin[path[len(NodePrefix):]]
 | 
				
			||||||
 | 
								if ldr == nil {
 | 
				
			||||||
 | 
									return nil, NoSuchBuiltInModuleError
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								withPrefix = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							isBuiltIn = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ldr != nil {
 | 
				
			||||||
 | 
							module = r.createModuleObject()
 | 
				
			||||||
 | 
							r.modules[path] = module
 | 
				
			||||||
 | 
							if isBuiltIn {
 | 
				
			||||||
 | 
								if withPrefix {
 | 
				
			||||||
 | 
									r.modules[path[len(NodePrefix):]] = module
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if !strings.HasPrefix(path, NodePrefix) {
 | 
				
			||||||
 | 
										r.modules[NodePrefix+path] = module
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ldr(r.runtime, module)
 | 
				
			||||||
 | 
							return module, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil, InvalidModuleError
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequireModule) loadAsFileOrDirectory(path string) (module *js.Object, err error) {
 | 
				
			||||||
 | 
						if module, err = r.loadAsFile(path); module != nil || err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return r.loadAsDirectory(path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequireModule) loadAsFile(path string) (module *js.Object, err error) {
 | 
				
			||||||
 | 
						if module, err = r.loadModule(path); module != nil || err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p := path + ".js"
 | 
				
			||||||
 | 
						if module, err = r.loadModule(p); module != nil || err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p = path + ".json"
 | 
				
			||||||
 | 
						return r.loadModule(p)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequireModule) loadIndex(modpath string) (module *js.Object, err error) {
 | 
				
			||||||
 | 
						p := path.Join(modpath, "index.js")
 | 
				
			||||||
 | 
						if module, err = r.loadModule(p); module != nil || err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p = path.Join(modpath, "index.json")
 | 
				
			||||||
 | 
						return r.loadModule(p)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequireModule) loadAsDirectory(modpath string) (module *js.Object, err error) {
 | 
				
			||||||
 | 
						p := path.Join(modpath, "package.json")
 | 
				
			||||||
 | 
						buf, err := r.r.getSource(p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return r.loadIndex(modpath)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var pkg struct {
 | 
				
			||||||
 | 
							Main string
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = json.Unmarshal(buf, &pkg)
 | 
				
			||||||
 | 
						if err != nil || len(pkg.Main) == 0 {
 | 
				
			||||||
 | 
							return r.loadIndex(modpath)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m := path.Join(modpath, pkg.Main)
 | 
				
			||||||
 | 
						if module, err = r.loadAsFile(m); module != nil || err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return r.loadIndex(m)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequireModule) loadNodeModule(modpath, start string) (*js.Object, error) {
 | 
				
			||||||
 | 
						return r.loadAsFileOrDirectory(path.Join(start, modpath))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequireModule) loadNodeModules(modpath, start string) (module *js.Object, err error) {
 | 
				
			||||||
 | 
						for _, dir := range r.r.globalFolders {
 | 
				
			||||||
 | 
							if module, err = r.loadNodeModule(modpath, dir); module != nil || err != nil {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							var p string
 | 
				
			||||||
 | 
							if path.Base(start) != "node_modules" {
 | 
				
			||||||
 | 
								p = path.Join(start, "node_modules")
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								p = start
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if module, err = r.loadNodeModule(modpath, p); module != nil || err != nil {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if start == ".." { // Dir('..') is '.'
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							parent := path.Dir(start)
 | 
				
			||||||
 | 
							if parent == start {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							start = parent
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequireModule) getCurrentModulePath() string {
 | 
				
			||||||
 | 
						var buf [2]js.StackFrame
 | 
				
			||||||
 | 
						frames := r.runtime.CaptureCallStack(2, buf[:0])
 | 
				
			||||||
 | 
						if len(frames) < 2 {
 | 
				
			||||||
 | 
							return "."
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return path.Dir(frames[1].SrcName())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequireModule) createModuleObject() *js.Object {
 | 
				
			||||||
 | 
						module := r.runtime.NewObject()
 | 
				
			||||||
 | 
						module.Set("exports", r.runtime.NewObject())
 | 
				
			||||||
 | 
						return module
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequireModule) loadModule(path string) (*js.Object, error) {
 | 
				
			||||||
 | 
						module := r.modules[path]
 | 
				
			||||||
 | 
						if module == nil {
 | 
				
			||||||
 | 
							module = r.createModuleObject()
 | 
				
			||||||
 | 
							r.modules[path] = module
 | 
				
			||||||
 | 
							err := r.loadModuleFile(path, module)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								module = nil
 | 
				
			||||||
 | 
								delete(r.modules, path)
 | 
				
			||||||
 | 
								if errors.Is(err, ModuleFileDoesNotExistError) {
 | 
				
			||||||
 | 
									err = nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return module, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return module, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequireModule) loadModuleFile(path string, jsModule *js.Object) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prg, err := r.r.getCompiledSource(path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f, err := r.runtime.RunProgram(prg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if call, ok := js.AssertFunction(f); ok {
 | 
				
			||||||
 | 
							jsExports := jsModule.Get("exports")
 | 
				
			||||||
 | 
							jsRequire := r.runtime.Get("require")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Run the module source, with "jsExports" as "this",
 | 
				
			||||||
 | 
							// "jsExports" as the "exports" variable, "jsRequire"
 | 
				
			||||||
 | 
							// as the "require" variable and "jsModule" as the
 | 
				
			||||||
 | 
							// "module" variable (Nodejs capable).
 | 
				
			||||||
 | 
							_, err = call(jsExports, jsExports, jsRequire, jsModule)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return InvalidModuleError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isFileOrDirectoryPath(path string) bool {
 | 
				
			||||||
 | 
						result := path == "." || path == ".." ||
 | 
				
			||||||
 | 
							strings.HasPrefix(path, "/") ||
 | 
				
			||||||
 | 
							strings.HasPrefix(path, "./") ||
 | 
				
			||||||
 | 
							strings.HasPrefix(path, "../")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if runtime.GOOS == "windows" {
 | 
				
			||||||
 | 
							result = result ||
 | 
				
			||||||
 | 
								strings.HasPrefix(path, `.\`) ||
 | 
				
			||||||
 | 
								strings.HasPrefix(path, `..\`) ||
 | 
				
			||||||
 | 
								filepath.IsAbs(path)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								goja_nodejs/require/testdata/m.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								goja_nodejs/require/testdata/m.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					function test() {
 | 
				
			||||||
 | 
					    return "passed";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					    test: test
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1
									
								
								goja_nodejs/staticcheck.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								goja_nodejs/staticcheck.conf
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					checks = ["all", "-ST1000", "-ST1003", "-ST1005", "-ST1006", "-ST1012", "-ST1021", "-ST1020", "-ST1008"]
 | 
				
			||||||
							
								
								
									
										134
									
								
								goja_nodejs/url/escape.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								goja_nodejs/url/escape.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					package url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var tblEscapeURLQuery = [128]byte{
 | 
				
			||||||
 | 
						0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
						0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
						0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
 | 
				
			||||||
 | 
						1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
 | 
				
			||||||
 | 
						1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 | 
				
			||||||
 | 
						1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 | 
				
			||||||
 | 
						1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 | 
				
			||||||
 | 
						1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var tblEscapeURLQueryParam = [128]byte{
 | 
				
			||||||
 | 
						0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
						0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
						0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0,
 | 
				
			||||||
 | 
						1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
						0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 | 
				
			||||||
 | 
						1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
 | 
				
			||||||
 | 
						0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 | 
				
			||||||
 | 
						1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The code below is mostly borrowed from the standard Go url package
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const upperhex = "0123456789ABCDEF"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ishex(c byte) bool {
 | 
				
			||||||
 | 
						switch {
 | 
				
			||||||
 | 
						case '0' <= c && c <= '9':
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						case 'a' <= c && c <= 'f':
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						case 'A' <= c && c <= 'F':
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func unhex(c byte) byte {
 | 
				
			||||||
 | 
						switch {
 | 
				
			||||||
 | 
						case '0' <= c && c <= '9':
 | 
				
			||||||
 | 
							return c - '0'
 | 
				
			||||||
 | 
						case 'a' <= c && c <= 'f':
 | 
				
			||||||
 | 
							return c - 'a' + 10
 | 
				
			||||||
 | 
						case 'A' <= c && c <= 'F':
 | 
				
			||||||
 | 
							return c - 'A' + 10
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func escape(s string, table *[128]byte, spaceToPlus bool) string {
 | 
				
			||||||
 | 
						spaceCount, hexCount := 0, 0
 | 
				
			||||||
 | 
						for i := 0; i < len(s); i++ {
 | 
				
			||||||
 | 
							c := s[i]
 | 
				
			||||||
 | 
							if c > 127 || table[c] == 0 {
 | 
				
			||||||
 | 
								if c == ' ' && spaceToPlus {
 | 
				
			||||||
 | 
									spaceCount++
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									hexCount++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if spaceCount == 0 && hexCount == 0 {
 | 
				
			||||||
 | 
							return s
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var sb strings.Builder
 | 
				
			||||||
 | 
						hexBuf := [3]byte{'%', 0, 0}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sb.Grow(len(s) + 2*hexCount)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := 0; i < len(s); i++ {
 | 
				
			||||||
 | 
							switch c := s[i]; {
 | 
				
			||||||
 | 
							case c == ' ' && spaceToPlus:
 | 
				
			||||||
 | 
								sb.WriteByte('+')
 | 
				
			||||||
 | 
							case c > 127 || table[c] == 0:
 | 
				
			||||||
 | 
								hexBuf[1] = upperhex[c>>4]
 | 
				
			||||||
 | 
								hexBuf[2] = upperhex[c&15]
 | 
				
			||||||
 | 
								sb.Write(hexBuf[:])
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								sb.WriteByte(c)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return sb.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func unescapeSearchParam(s string) string {
 | 
				
			||||||
 | 
						n := 0
 | 
				
			||||||
 | 
						hasPlus := false
 | 
				
			||||||
 | 
						for i := 0; i < len(s); {
 | 
				
			||||||
 | 
							switch s[i] {
 | 
				
			||||||
 | 
							case '%':
 | 
				
			||||||
 | 
								if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
 | 
				
			||||||
 | 
									i++
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								n++
 | 
				
			||||||
 | 
								i += 3
 | 
				
			||||||
 | 
							case '+':
 | 
				
			||||||
 | 
								hasPlus = true
 | 
				
			||||||
 | 
								i++
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								i++
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if n == 0 && !hasPlus {
 | 
				
			||||||
 | 
							return s
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var t strings.Builder
 | 
				
			||||||
 | 
						t.Grow(len(s) - 2*n)
 | 
				
			||||||
 | 
						for i := 0; i < len(s); i++ {
 | 
				
			||||||
 | 
							switch s[i] {
 | 
				
			||||||
 | 
							case '%':
 | 
				
			||||||
 | 
								if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
 | 
				
			||||||
 | 
									t.WriteByte('%')
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									t.WriteByte(unhex(s[i+1])<<4 | unhex(s[i+2]))
 | 
				
			||||||
 | 
									i += 2
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case '+':
 | 
				
			||||||
 | 
								t.WriteByte(' ')
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								t.WriteByte(s[i])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return t.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										36
									
								
								goja_nodejs/url/module.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								goja_nodejs/url/module.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					package url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ModuleName = "url"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type urlModule struct {
 | 
				
			||||||
 | 
						r *goja.Runtime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						URLSearchParamsPrototype         *goja.Object
 | 
				
			||||||
 | 
						URLSearchParamsIteratorPrototype *goja.Object
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Require(runtime *goja.Runtime, module *goja.Object) {
 | 
				
			||||||
 | 
						exports := module.Get("exports").(*goja.Object)
 | 
				
			||||||
 | 
						m := &urlModule{
 | 
				
			||||||
 | 
							r: runtime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						exports.Set("URL", m.createURLConstructor())
 | 
				
			||||||
 | 
						exports.Set("URLSearchParams", m.createURLSearchParamsConstructor())
 | 
				
			||||||
 | 
						exports.Set("domainToASCII", m.domainToASCII)
 | 
				
			||||||
 | 
						exports.Set("domainToUnicode", m.domainToUnicode)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Enable(runtime *goja.Runtime) {
 | 
				
			||||||
 | 
						m := require.Require(runtime, ModuleName).ToObject(runtime)
 | 
				
			||||||
 | 
						runtime.Set("URL", m.Get("URL"))
 | 
				
			||||||
 | 
						runtime.Set("URLSearchParams", m.Get("URLSearchParams"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						require.RegisterCoreModule(ModuleName, Require)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										148
									
								
								goja_nodejs/url/nodeurl.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								goja_nodejs/url/nodeurl.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,148 @@
 | 
				
			|||||||
 | 
					package url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type searchParam struct {
 | 
				
			||||||
 | 
						name  string
 | 
				
			||||||
 | 
						value string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (sp *searchParam) Encode() string {
 | 
				
			||||||
 | 
						return sp.string(true)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func escapeSearchParam(s string) string {
 | 
				
			||||||
 | 
						return escape(s, &tblEscapeURLQueryParam, true)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (sp *searchParam) string(encode bool) string {
 | 
				
			||||||
 | 
						if encode {
 | 
				
			||||||
 | 
							return escapeSearchParam(sp.name) + "=" + escapeSearchParam(sp.value)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return sp.name + "=" + sp.value
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type searchParams []searchParam
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s searchParams) Len() int {
 | 
				
			||||||
 | 
						return len(s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s searchParams) Swap(i, j int) {
 | 
				
			||||||
 | 
						s[i], s[j] = s[j], s[i]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s searchParams) Less(i, j int) bool {
 | 
				
			||||||
 | 
						return strings.Compare(s[i].name, s[j].name) < 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s searchParams) Encode() string {
 | 
				
			||||||
 | 
						var sb strings.Builder
 | 
				
			||||||
 | 
						for i, v := range s {
 | 
				
			||||||
 | 
							if i > 0 {
 | 
				
			||||||
 | 
								sb.WriteByte('&')
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							sb.WriteString(v.Encode())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return sb.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s searchParams) String() string {
 | 
				
			||||||
 | 
						var sb strings.Builder
 | 
				
			||||||
 | 
						for i, v := range s {
 | 
				
			||||||
 | 
							if i > 0 {
 | 
				
			||||||
 | 
								sb.WriteByte('&')
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							sb.WriteString(v.string(false))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return sb.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type nodeURL struct {
 | 
				
			||||||
 | 
						url          *url.URL
 | 
				
			||||||
 | 
						searchParams searchParams
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type urlSearchParams nodeURL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This methods ensures that the url.URL has the proper RawQuery based on the searchParam
 | 
				
			||||||
 | 
					// structs. If a change is made to the searchParams we need to keep them in sync.
 | 
				
			||||||
 | 
					func (nu *nodeURL) syncSearchParams() {
 | 
				
			||||||
 | 
						if nu.rawQueryUpdateNeeded() {
 | 
				
			||||||
 | 
							nu.url.RawQuery = nu.searchParams.Encode()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (nu *nodeURL) rawQueryUpdateNeeded() bool {
 | 
				
			||||||
 | 
						return len(nu.searchParams) > 0 && nu.url.RawQuery == ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (nu *nodeURL) String() string {
 | 
				
			||||||
 | 
						return nu.url.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (sp *urlSearchParams) hasName(name string) bool {
 | 
				
			||||||
 | 
						for _, v := range sp.searchParams {
 | 
				
			||||||
 | 
							if v.name == name {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (sp *urlSearchParams) hasValue(name, value string) bool {
 | 
				
			||||||
 | 
						for _, v := range sp.searchParams {
 | 
				
			||||||
 | 
							if v.name == name && v.value == value {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (sp *urlSearchParams) getValues(name string) []string {
 | 
				
			||||||
 | 
						vals := make([]string, 0, len(sp.searchParams))
 | 
				
			||||||
 | 
						for _, v := range sp.searchParams {
 | 
				
			||||||
 | 
							if v.name == name {
 | 
				
			||||||
 | 
								vals = append(vals, v.value)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return vals
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (sp *urlSearchParams) getFirstValue(name string) (string, bool) {
 | 
				
			||||||
 | 
						for _, v := range sp.searchParams {
 | 
				
			||||||
 | 
							if v.name == name {
 | 
				
			||||||
 | 
								return v.value, true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return "", false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func parseSearchQuery(query string) (ret searchParams) {
 | 
				
			||||||
 | 
						if query == "" {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query = strings.TrimPrefix(query, "?")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, v := range strings.Split(query, "&") {
 | 
				
			||||||
 | 
							if v == "" {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							pair := strings.SplitN(v, "=", 2)
 | 
				
			||||||
 | 
							l := len(pair)
 | 
				
			||||||
 | 
							if l == 1 {
 | 
				
			||||||
 | 
								ret = append(ret, searchParam{name: unescapeSearchParam(pair[0]), value: ""})
 | 
				
			||||||
 | 
							} else if l == 2 {
 | 
				
			||||||
 | 
								ret = append(ret, searchParam{name: unescapeSearchParam(pair[0]), value: unescapeSearchParam(pair[1])})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										385
									
								
								goja_nodejs/url/testdata/url_search_params.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										385
									
								
								goja_nodejs/url/testdata/url_search_params.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,385 @@
 | 
				
			|||||||
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const assert = require("../../assert.js");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function testCtor(value, expected) {
 | 
				
			||||||
 | 
					  assert.sameValue(new URLSearchParams(value).toString(), expected);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					testCtor("user=abc&query=xyz", "user=abc&query=xyz");
 | 
				
			||||||
 | 
					testCtor("?user=abc&query=xyz", "user=abc&query=xyz");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					testCtor(
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    num: 1,
 | 
				
			||||||
 | 
					    user: "abc",
 | 
				
			||||||
 | 
					    query: ["first", "second"],
 | 
				
			||||||
 | 
					    obj: { prop: "value" },
 | 
				
			||||||
 | 
					    b: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "num=1&user=abc&query=first%2Csecond&obj=%5Bobject+Object%5D&b=true"
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const map = new Map();
 | 
				
			||||||
 | 
					map.set("user", "abc");
 | 
				
			||||||
 | 
					map.set("query", "xyz");
 | 
				
			||||||
 | 
					testCtor(map, "user=abc&query=xyz");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					testCtor(
 | 
				
			||||||
 | 
					  [
 | 
				
			||||||
 | 
					    ["user", "abc"],
 | 
				
			||||||
 | 
					    ["query", "first"],
 | 
				
			||||||
 | 
					    ["query", "second"],
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "user=abc&query=first&query=second"
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Each key-value pair must have exactly two elements
 | 
				
			||||||
 | 
					assert.throwsNodeError(() => new URLSearchParams([["single_value"]]), TypeError, "ERR_INVALID_TUPLE");
 | 
				
			||||||
 | 
					assert.throwsNodeError(() => new URLSearchParams([["too", "many", "values"]]), TypeError, "ERR_INVALID_TUPLE");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("a=b&cc=d");
 | 
				
			||||||
 | 
					params.forEach((value, name, searchParams) => {
 | 
				
			||||||
 | 
					  if (name === "a") {
 | 
				
			||||||
 | 
					    assert.sameValue(value, "b");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (name === "cc") {
 | 
				
			||||||
 | 
					    assert.sameValue(value, "d");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  assert.sameValue(searchParams, params);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params.forEach((value, name, searchParams) => {
 | 
				
			||||||
 | 
					    if (name === "a") {
 | 
				
			||||||
 | 
					        assert.sameValue(value, "b");
 | 
				
			||||||
 | 
					        searchParams.set("cc", "d1");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (name === "cc") {
 | 
				
			||||||
 | 
					        assert.sameValue(value, "d1");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    assert.sameValue(searchParams, params);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					assert.throwsNodeError(() => params.forEach(123), TypeError, "ERR_INVALID_ARG_TYPE");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					assert.throwsNodeError(() => params.forEach.call(1, 2), TypeError, "ERR_INVALID_THIS");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("a=1=2&b=3");
 | 
				
			||||||
 | 
					assert.sameValue(params.size, 2);
 | 
				
			||||||
 | 
					assert.sameValue(params.get("a"), "1=2");
 | 
				
			||||||
 | 
					assert.sameValue(params.get("b"), "3");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("&");
 | 
				
			||||||
 | 
					assert.sameValue(params.size, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("& ");
 | 
				
			||||||
 | 
					assert.sameValue(params.size, 1);
 | 
				
			||||||
 | 
					assert.sameValue(params.get(" "), "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams(" &");
 | 
				
			||||||
 | 
					assert.sameValue(params.size, 1);
 | 
				
			||||||
 | 
					assert.sameValue(params.get(" "), "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("=");
 | 
				
			||||||
 | 
					assert.sameValue(params.size, 1);
 | 
				
			||||||
 | 
					assert.sameValue(params.get(""), "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("&=2");
 | 
				
			||||||
 | 
					assert.sameValue(params.size, 1);
 | 
				
			||||||
 | 
					assert.sameValue(params.get(""), "2");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("?user=abc");
 | 
				
			||||||
 | 
					assert.throwsNodeError(() => params.append(), TypeError, "ERR_MISSING_ARGS");
 | 
				
			||||||
 | 
					params.append("query", "first");
 | 
				
			||||||
 | 
					assert.sameValue(params.toString(), "user=abc&query=first");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("first=one&second=two&third=three");
 | 
				
			||||||
 | 
					assert.throwsNodeError(() => params.delete(), TypeError, "ERR_MISSING_ARGS");
 | 
				
			||||||
 | 
					params.delete("second", "fake-value");
 | 
				
			||||||
 | 
					assert.sameValue(params.toString(), "first=one&second=two&third=three");
 | 
				
			||||||
 | 
					params.delete("third", "three");
 | 
				
			||||||
 | 
					assert.sameValue(params.toString(), "first=one&second=two");
 | 
				
			||||||
 | 
					params.delete("second");
 | 
				
			||||||
 | 
					assert.sameValue(params.toString(), "first=one");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("user=abc&query=xyz");
 | 
				
			||||||
 | 
					assert.throwsNodeError(() => params.get(), TypeError, "ERR_MISSING_ARGS");
 | 
				
			||||||
 | 
					assert.sameValue(params.get("user"), "abc");
 | 
				
			||||||
 | 
					assert.sameValue(params.get("non-existant"), null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("query=first&query=second");
 | 
				
			||||||
 | 
					assert.throwsNodeError(() => params.getAll(), TypeError, "ERR_MISSING_ARGS");
 | 
				
			||||||
 | 
					const all = params.getAll("query");
 | 
				
			||||||
 | 
					assert.sameValue(all.includes("first"), true);
 | 
				
			||||||
 | 
					assert.sameValue(all.includes("second"), true);
 | 
				
			||||||
 | 
					assert.sameValue(all.length, 2);
 | 
				
			||||||
 | 
					const getAllUndefined = params.getAll(undefined);
 | 
				
			||||||
 | 
					assert.sameValue(getAllUndefined.length, 0);
 | 
				
			||||||
 | 
					const getAllNonExistant = params.getAll("does_not_exists");
 | 
				
			||||||
 | 
					assert.sameValue(getAllNonExistant.length, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("user=abc&query=xyz");
 | 
				
			||||||
 | 
					assert.throwsNodeError(() => params.has(), TypeError, "ERR_MISSING_ARGS");
 | 
				
			||||||
 | 
					assert.sameValue(params.has(undefined), false);
 | 
				
			||||||
 | 
					assert.sameValue(params.has("user"), true);
 | 
				
			||||||
 | 
					assert.sameValue(params.has("user", "abc"), true);
 | 
				
			||||||
 | 
					assert.sameValue(params.has("user", "abc", "extra-param"), true);
 | 
				
			||||||
 | 
					assert.sameValue(params.has("user", "efg"), false);
 | 
				
			||||||
 | 
					assert.sameValue(params.has("user", undefined), true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams();
 | 
				
			||||||
 | 
					params.append("foo", "bar");
 | 
				
			||||||
 | 
					params.append("foo", "baz");
 | 
				
			||||||
 | 
					params.append("abc", "def");
 | 
				
			||||||
 | 
					assert.sameValue(params.toString(), "foo=bar&foo=baz&abc=def");
 | 
				
			||||||
 | 
					params.set("foo", "def");
 | 
				
			||||||
 | 
					params.set("xyz", "opq");
 | 
				
			||||||
 | 
					assert.sameValue(params.toString(), "foo=def&abc=def&xyz=opq");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("query=first&query=second&user=abc&double=first,second");
 | 
				
			||||||
 | 
					const URLSearchIteratorPrototype = params.entries().__proto__;
 | 
				
			||||||
 | 
					assert.sameValue(typeof URLSearchIteratorPrototype, "object");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					assert.sameValue(params[Symbol.iterator], params.entries);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const entries = params.entries();
 | 
				
			||||||
 | 
					    assert.sameValue(entries.toString(), "[object URLSearchParams Iterator]");
 | 
				
			||||||
 | 
					    assert.sameValue(entries.__proto__, URLSearchIteratorPrototype);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let item = entries.next();
 | 
				
			||||||
 | 
					    assert.sameValue(item.value.toString(), ["query", "first"].toString());
 | 
				
			||||||
 | 
					    assert.sameValue(item.done, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    item = entries.next();
 | 
				
			||||||
 | 
					    assert.sameValue(item.value.toString(), ["query", "second"].toString());
 | 
				
			||||||
 | 
					    assert.sameValue(item.done, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    item = entries.next();
 | 
				
			||||||
 | 
					    assert.sameValue(item.value.toString(), ["user", "abc"].toString());
 | 
				
			||||||
 | 
					    assert.sameValue(item.done, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    item = entries.next();
 | 
				
			||||||
 | 
					    assert.sameValue(item.value.toString(), ["double", "first,second"].toString());
 | 
				
			||||||
 | 
					    assert.sameValue(item.done, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    item = entries.next();
 | 
				
			||||||
 | 
					    assert.sameValue(item.value, undefined);
 | 
				
			||||||
 | 
					    assert.sameValue(item.done, true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("query=first&query=second&user=abc");
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const keys = params.keys();
 | 
				
			||||||
 | 
					    assert.sameValue(keys.__proto__, URLSearchIteratorPrototype);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let item = keys.next();
 | 
				
			||||||
 | 
					    assert.sameValue(item.value, "query");
 | 
				
			||||||
 | 
					    assert.sameValue(item.done, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    item = keys.next();
 | 
				
			||||||
 | 
					    assert.sameValue(item.value, "query");
 | 
				
			||||||
 | 
					    assert.sameValue(item.done, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    item = keys.next();
 | 
				
			||||||
 | 
					    assert.sameValue(item.value, "user");
 | 
				
			||||||
 | 
					    assert.sameValue(item.done, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    item = keys.next();
 | 
				
			||||||
 | 
					    assert.sameValue(item.value, undefined);
 | 
				
			||||||
 | 
					    assert.sameValue(item.done, true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("query=first&query=second&user=abc");
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const values = params.values();
 | 
				
			||||||
 | 
					    assert.sameValue(values.__proto__, URLSearchIteratorPrototype);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let item = values.next();
 | 
				
			||||||
 | 
					    assert.sameValue(item.value, "first");
 | 
				
			||||||
 | 
					    assert.sameValue(item.done, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    item = values.next();
 | 
				
			||||||
 | 
					    assert.sameValue(item.value, "second");
 | 
				
			||||||
 | 
					    assert.sameValue(item.done, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    item = values.next();
 | 
				
			||||||
 | 
					    assert.sameValue(item.value, "abc");
 | 
				
			||||||
 | 
					    assert.sameValue(item.done, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    item = values.next();
 | 
				
			||||||
 | 
					    assert.sameValue(item.value, undefined);
 | 
				
			||||||
 | 
					    assert.sameValue(item.done, true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("query[]=abc&type=search&query[]=123");
 | 
				
			||||||
 | 
					params.sort();
 | 
				
			||||||
 | 
					assert.sameValue(params.toString(), "query%5B%5D=abc&query%5B%5D=123&type=search");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("query=first&query=second&user=abc");
 | 
				
			||||||
 | 
					assert.sameValue(params.size, 3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams("%");
 | 
				
			||||||
 | 
					assert.sameValue(params.has("%"), true);
 | 
				
			||||||
 | 
					assert.sameValue(params.toString(), "%25=");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const params = new URLSearchParams("");
 | 
				
			||||||
 | 
					    assert.sameValue(params.size, 0);
 | 
				
			||||||
 | 
					    assert.sameValue(params.toString(), "");
 | 
				
			||||||
 | 
					    assert.sameValue(params.get(undefined), null);
 | 
				
			||||||
 | 
					    params.set(undefined, true);
 | 
				
			||||||
 | 
					    assert.sameValue(params.has(undefined), true);
 | 
				
			||||||
 | 
					    assert.sameValue(params.has("undefined"), true);
 | 
				
			||||||
 | 
					    assert.sameValue(params.get("undefined"), "true");
 | 
				
			||||||
 | 
					    assert.sameValue(params.get(undefined), "true");
 | 
				
			||||||
 | 
					    assert.sameValue(params.getAll(undefined).toString(), ["true"].toString());
 | 
				
			||||||
 | 
					    params.delete(undefined);
 | 
				
			||||||
 | 
					    assert.sameValue(params.has(undefined), false);
 | 
				
			||||||
 | 
					    assert.sameValue(params.has("undefined"), false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert.sameValue(params.has(null), false);
 | 
				
			||||||
 | 
					    params.set(null, "nullval");
 | 
				
			||||||
 | 
					    assert.sameValue(params.has(null), true);
 | 
				
			||||||
 | 
					    assert.sameValue(params.has("null"), true);
 | 
				
			||||||
 | 
					    assert.sameValue(params.get(null), "nullval");
 | 
				
			||||||
 | 
					    assert.sameValue(params.get("null"), "nullval");
 | 
				
			||||||
 | 
					    params.delete(null);
 | 
				
			||||||
 | 
					    assert.sameValue(params.has(null), false);
 | 
				
			||||||
 | 
					    assert.sameValue(params.has("null"), false);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function* functionGeneratorExample() {
 | 
				
			||||||
 | 
					  yield ["user", "abc"];
 | 
				
			||||||
 | 
					  yield ["query", "first"];
 | 
				
			||||||
 | 
					  yield ["query", "second"];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					params = new URLSearchParams(functionGeneratorExample());
 | 
				
			||||||
 | 
					assert.sameValue(params.toString(), "user=abc&query=first&query=second");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					assert.sameValue(params.__proto__.constructor, URLSearchParams);
 | 
				
			||||||
 | 
					assert.sameValue(params instanceof URLSearchParams, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const params = new URLSearchParams("1=2&1=3");
 | 
				
			||||||
 | 
					    assert.sameValue(params.get(1), "2");
 | 
				
			||||||
 | 
					    assert.sameValue(params.getAll(1).toString(), ["2", "3"].toString());
 | 
				
			||||||
 | 
					    assert.sameValue(params.getAll("x").toString(), [].toString());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Sync
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const url = new URL("https://test.com/");
 | 
				
			||||||
 | 
					    const params = url.searchParams;
 | 
				
			||||||
 | 
					    assert.sameValue(params.size, 0);
 | 
				
			||||||
 | 
					    url.search = "a=1";
 | 
				
			||||||
 | 
					    assert.sameValue(params.size, 1);
 | 
				
			||||||
 | 
					    assert.sameValue(params.get("a"), "1");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const url = new URL("https://test.com/?a=1");
 | 
				
			||||||
 | 
					    const params = url.searchParams;
 | 
				
			||||||
 | 
					    assert.sameValue(params.size, 1);
 | 
				
			||||||
 | 
					    url.search = "";
 | 
				
			||||||
 | 
					    assert.sameValue(params.size, 0);
 | 
				
			||||||
 | 
					    url.search = "b=2";
 | 
				
			||||||
 | 
					    assert.sameValue(params.size, 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const url = new URL("https://test.com/");
 | 
				
			||||||
 | 
					    const params = url.searchParams;
 | 
				
			||||||
 | 
					    params.append("a", "1");
 | 
				
			||||||
 | 
					    assert.sameValue(url.toString(), "https://test.com/?a=1");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const url = new URL("https://test.com/");
 | 
				
			||||||
 | 
					    url.searchParams.append("a", "1");
 | 
				
			||||||
 | 
					    url.searchParams.append("b", "1");
 | 
				
			||||||
 | 
					    assert.sameValue(url.toString(), "https://test.com/?a=1&b=1");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const url = new URL("https://test.com/");
 | 
				
			||||||
 | 
					    const params = url.searchParams;
 | 
				
			||||||
 | 
					    url.searchParams.append("a", "1");
 | 
				
			||||||
 | 
					    assert.sameValue(url.search, "?a=1");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const url = new URL("https://test.com/?a=1");
 | 
				
			||||||
 | 
					    const params = url.searchParams;
 | 
				
			||||||
 | 
					    params.append("a", "2");
 | 
				
			||||||
 | 
					    assert.sameValue(url.search, "?a=1&a=2");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const url = new URL("https://test.com/");
 | 
				
			||||||
 | 
					    const params = url.searchParams;
 | 
				
			||||||
 | 
					    params.set("a", "1");
 | 
				
			||||||
 | 
					    assert.sameValue(url.search, "?a=1");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const url = new URL("https://test.com/");
 | 
				
			||||||
 | 
					    url.searchParams.set("a", "1");
 | 
				
			||||||
 | 
					    url.searchParams.set("b", "1");
 | 
				
			||||||
 | 
					    assert.sameValue(url.toString(), "https://test.com/?a=1&b=1");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const url = new URL("https://test.com/?a=1&b=2");
 | 
				
			||||||
 | 
					    const params = url.searchParams;
 | 
				
			||||||
 | 
					    params.delete("a");
 | 
				
			||||||
 | 
					    assert.sameValue(url.search, "?b=2");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const url = new URL("https://test.com/?b=2&a=1");
 | 
				
			||||||
 | 
					    const params = url.searchParams;
 | 
				
			||||||
 | 
					    params.sort();
 | 
				
			||||||
 | 
					    assert.sameValue(url.search, "?a=1&b=2");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const url = new URL("https://test.com/?a=1");
 | 
				
			||||||
 | 
					    const params = url.searchParams;
 | 
				
			||||||
 | 
					    params.delete("a");
 | 
				
			||||||
 | 
					    assert.sameValue(url.search, "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    params.set("a", 2);
 | 
				
			||||||
 | 
					    assert.sameValue(url.search, "?a=2");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FAILING: no custom properties on wrapped Go structs
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const params = new URLSearchParams("");
 | 
				
			||||||
 | 
					    assert.sameValue(Object.isExtensible(params), true);
 | 
				
			||||||
 | 
					    assert.sameValue(Reflect.defineProperty(params, "customField", {value: 42, configurable: true}), true);
 | 
				
			||||||
 | 
					    assert.sameValue(params.customField, 42);
 | 
				
			||||||
 | 
					    const desc = Reflect.getOwnPropertyDescriptor(params, "customField");
 | 
				
			||||||
 | 
					    assert.sameValue(desc.value, 42);
 | 
				
			||||||
 | 
					    assert.sameValue(desc.writable, false);
 | 
				
			||||||
 | 
					    assert.sameValue(desc.enumerable, false);
 | 
				
			||||||
 | 
					    assert.sameValue(desc.configurable, true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Escape
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const myURL = new URL('https://example.org/abc?fo~o=~ba r%z');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert.sameValue(myURL.search, "?fo~o=~ba%20r%z");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Modify the URL via searchParams...
 | 
				
			||||||
 | 
					    myURL.searchParams.sort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert.sameValue(myURL.search, "?fo%7Eo=%7Eba+r%25z");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										229
									
								
								goja_nodejs/url/testdata/url_test.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								goja_nodejs/url/testdata/url_test.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,229 @@
 | 
				
			|||||||
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const assert = require("../../assert.js");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function testURLCtor(str, expected) {
 | 
				
			||||||
 | 
					  assert.sameValue(new URL(str).toString(), expected);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function testURLCtorBase(ref, base, expected, message) {
 | 
				
			||||||
 | 
					  assert.sameValue(new URL(ref, base).toString(), expected, message);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					testURLCtorBase("https://example.org/", undefined, "https://example.org/");
 | 
				
			||||||
 | 
					testURLCtorBase("/foo", "https://example.org/", "https://example.org/foo");
 | 
				
			||||||
 | 
					testURLCtorBase("http://Example.com/", "https://example.org/", "http://example.com/");
 | 
				
			||||||
 | 
					testURLCtorBase("https://Example.com/", "https://example.org/", "https://example.com/");
 | 
				
			||||||
 | 
					testURLCtorBase("foo://Example.com/", "https://example.org/", "foo://Example.com/");
 | 
				
			||||||
 | 
					testURLCtorBase("foo:Example.com/", "https://example.org/", "foo:Example.com/");
 | 
				
			||||||
 | 
					testURLCtorBase("#hash", "https://example.org/", "https://example.org/#hash");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					testURLCtor("HTTP://test.com", "http://test.com/");
 | 
				
			||||||
 | 
					testURLCtor("HTTPS://á.com", "https://xn--1ca.com/");
 | 
				
			||||||
 | 
					testURLCtor("HTTPS://á.com:123", "https://xn--1ca.com:123/");
 | 
				
			||||||
 | 
					testURLCtor("https://test.com#asdfá", "https://test.com/#asdf%C3%A1");
 | 
				
			||||||
 | 
					testURLCtor("HTTPS://á.com:123/á", "https://xn--1ca.com:123/%C3%A1");
 | 
				
			||||||
 | 
					testURLCtor("fish://á.com", "fish://%C3%A1.com");
 | 
				
			||||||
 | 
					testURLCtor("https://test.com/?a=1 /2", "https://test.com/?a=1%20/2");
 | 
				
			||||||
 | 
					testURLCtor("https://test.com/á=1?á=1&ü=2#é", "https://test.com/%C3%A1=1?%C3%A1=1&%C3%BC=2#%C3%A9");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					assert.throws(() => new URL("test"), TypeError);
 | 
				
			||||||
 | 
					assert.throws(() => new URL("ssh://EEE:ddd"), TypeError);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    let u = new URL("https://example.org/");
 | 
				
			||||||
 | 
					    assert.sameValue(u.__proto__.constructor, URL);
 | 
				
			||||||
 | 
					    assert.sameValue(u instanceof URL, true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    let u = new URL("https://example.org/");
 | 
				
			||||||
 | 
					    assert.sameValue(u.searchParams, u.searchParams);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let myURL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Hash
 | 
				
			||||||
 | 
					myURL = new URL("https://example.org/foo#bar");
 | 
				
			||||||
 | 
					myURL.hash = "baz";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.org/foo#baz");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					myURL.hash = "#baz";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.org/foo#baz");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					myURL.hash = "#á=1 2";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.org/foo#%C3%A1=1%202");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					myURL.hash = "#a/#b";
 | 
				
			||||||
 | 
					// FAILING: the second # gets escaped
 | 
				
			||||||
 | 
					//assert.sameValue(myURL.href, "https://example.org/foo#a/#b");
 | 
				
			||||||
 | 
					assert.sameValue(myURL.search, "");
 | 
				
			||||||
 | 
					// FAILING: the second # gets escaped
 | 
				
			||||||
 | 
					//assert.sameValue(myURL.hash, "#a/#b");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Host
 | 
				
			||||||
 | 
					myURL = new URL("https://example.org:81/foo");
 | 
				
			||||||
 | 
					myURL.host = "example.com:82";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.com:82/foo");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Hostname
 | 
				
			||||||
 | 
					myURL = new URL("https://example.org:81/foo");
 | 
				
			||||||
 | 
					myURL.hostname = "example.com:82";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.org:81/foo");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					myURL.hostname = "á.com";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://xn--1ca.com:81/foo");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// href
 | 
				
			||||||
 | 
					myURL = new URL("https://example.org/foo");
 | 
				
			||||||
 | 
					myURL.href = "https://example.com/bar";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.com/bar");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Password
 | 
				
			||||||
 | 
					myURL = new URL("https://abc:xyz@example.com");
 | 
				
			||||||
 | 
					myURL.password = "123";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://abc:123@example.com/");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// pathname
 | 
				
			||||||
 | 
					myURL = new URL("https://example.org/abc/xyz?123");
 | 
				
			||||||
 | 
					myURL.pathname = "/abcdef";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.org/abcdef?123");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					myURL.pathname = "";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.org/?123");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					myURL.pathname = "á";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.pathname, "/%C3%A1");
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.org/%C3%A1?123");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// port
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					myURL = new URL("https://example.org:8888");
 | 
				
			||||||
 | 
					assert.sameValue(myURL.port, "8888");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function testSetPort(port, expected) {
 | 
				
			||||||
 | 
					  const url = new URL("https://example.org:8888");
 | 
				
			||||||
 | 
					  url.port = port;
 | 
				
			||||||
 | 
					  assert.sameValue(url.port, expected);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					testSetPort(0, "0");
 | 
				
			||||||
 | 
					testSetPort(-0, "0");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Default ports are automatically transformed to the empty string
 | 
				
			||||||
 | 
					// (HTTPS protocol's default port is 443)
 | 
				
			||||||
 | 
					testSetPort("443", "");
 | 
				
			||||||
 | 
					testSetPort(443, "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Empty string is the same as default port
 | 
				
			||||||
 | 
					testSetPort("", "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Completely invalid port strings are ignored
 | 
				
			||||||
 | 
					testSetPort("abcd", "8888");
 | 
				
			||||||
 | 
					testSetPort("-123", "");
 | 
				
			||||||
 | 
					testSetPort(-123, "");
 | 
				
			||||||
 | 
					testSetPort(-123.45, "");
 | 
				
			||||||
 | 
					testSetPort(undefined, "8888");
 | 
				
			||||||
 | 
					testSetPort(null, "8888");
 | 
				
			||||||
 | 
					testSetPort(+Infinity, "8888");
 | 
				
			||||||
 | 
					testSetPort(-Infinity, "8888");
 | 
				
			||||||
 | 
					testSetPort(NaN, "8888");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Leading numbers are treated as a port number
 | 
				
			||||||
 | 
					testSetPort("5678abcd", "5678");
 | 
				
			||||||
 | 
					testSetPort("a5678abcd", "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Non-integers are truncated
 | 
				
			||||||
 | 
					testSetPort(1234.5678, "1234");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Out-of-range numbers which are not represented in scientific notation
 | 
				
			||||||
 | 
					// will be ignored.
 | 
				
			||||||
 | 
					testSetPort(1e10, "8888");
 | 
				
			||||||
 | 
					testSetPort("123456", "8888");
 | 
				
			||||||
 | 
					testSetPort(123456, "8888");
 | 
				
			||||||
 | 
					testSetPort(4.567e21, "4");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// toString() takes precedence over valueOf(), even if it returns a valid integer
 | 
				
			||||||
 | 
					testSetPort(
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    toString() {
 | 
				
			||||||
 | 
					      return "2";
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    valueOf() {
 | 
				
			||||||
 | 
					      return 1;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "2"
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Protocol
 | 
				
			||||||
 | 
					function testSetProtocol(url, protocol, expected) {
 | 
				
			||||||
 | 
					  url.protocol = protocol;
 | 
				
			||||||
 | 
					  assert.sameValue(url.protocol, expected);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					testSetProtocol(new URL("https://example.org"), "ftp", "ftp:");
 | 
				
			||||||
 | 
					testSetProtocol(new URL("https://example.org"), "ftp:", "ftp:");
 | 
				
			||||||
 | 
					testSetProtocol(new URL("https://example.org"), "FTP:", "ftp:");
 | 
				
			||||||
 | 
					testSetProtocol(new URL("https://example.org"), "ftp: blah", "ftp:");
 | 
				
			||||||
 | 
					// special to non-special
 | 
				
			||||||
 | 
					testSetProtocol(new URL("https://example.org"), "foo", "https:");
 | 
				
			||||||
 | 
					// non-special to special
 | 
				
			||||||
 | 
					testSetProtocol(new URL("fish://example.org"), "https", "fish:");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Search
 | 
				
			||||||
 | 
					myURL = new URL("https://example.org/abc?123");
 | 
				
			||||||
 | 
					myURL.search = "abc=xyz";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.org/abc?abc=xyz");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					myURL.search = "a=1 2";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.org/abc?a=1%202");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					myURL.search = "á=ú";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.search, "?%C3%A1=%C3%BA");
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.org/abc?%C3%A1=%C3%BA");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					myURL.hash = "hash";
 | 
				
			||||||
 | 
					myURL.search = "a=#b";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.org/abc?a=%23b#hash");
 | 
				
			||||||
 | 
					assert.sameValue(myURL.search, "?a=%23b");
 | 
				
			||||||
 | 
					assert.sameValue(myURL.hash, "#hash");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Username
 | 
				
			||||||
 | 
					myURL = new URL("https://abc:xyz@example.com/");
 | 
				
			||||||
 | 
					myURL.username = "123";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://123:xyz@example.com/");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Origin, read-only
 | 
				
			||||||
 | 
					assert.throws(() => {
 | 
				
			||||||
 | 
					  myURL.origin = "abc";
 | 
				
			||||||
 | 
					}, TypeError);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// href
 | 
				
			||||||
 | 
					myURL = new URL("https://example.org");
 | 
				
			||||||
 | 
					myURL.href = "https://example.com";
 | 
				
			||||||
 | 
					assert.sameValue(myURL.href, "https://example.com/");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					assert.throws(() => {
 | 
				
			||||||
 | 
					  myURL.href = "test";
 | 
				
			||||||
 | 
					}, TypeError);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Search Params
 | 
				
			||||||
 | 
					myURL = new URL("https://example.com/");
 | 
				
			||||||
 | 
					myURL.searchParams.append("user", "abc");
 | 
				
			||||||
 | 
					assert.sameValue(myURL.toString(), "https://example.com/?user=abc");
 | 
				
			||||||
 | 
					myURL.searchParams.append("first", "one");
 | 
				
			||||||
 | 
					assert.sameValue(myURL.toString(), "https://example.com/?user=abc&first=one");
 | 
				
			||||||
 | 
					myURL.searchParams.delete("user");
 | 
				
			||||||
 | 
					assert.sameValue(myURL.toString(), "https://example.com/?first=one");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const url = require("url");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert.sameValue(url.domainToASCII('español.com'), "xn--espaol-zwa.com");
 | 
				
			||||||
 | 
					    assert.sameValue(url.domainToASCII('中文.com'), "xn--fiq228c.com");
 | 
				
			||||||
 | 
					    assert.sameValue(url.domainToASCII('xn--iñvalid.com'), "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert.sameValue(url.domainToUnicode('xn--espaol-zwa.com'), "español.com");
 | 
				
			||||||
 | 
					    assert.sameValue(url.domainToUnicode('xn--fiq228c.com'), "中文.com");
 | 
				
			||||||
 | 
					    assert.sameValue(url.domainToUnicode('xn--iñvalid.com'), "");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										407
									
								
								goja_nodejs/url/url.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										407
									
								
								goja_nodejs/url/url.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,407 @@
 | 
				
			|||||||
 | 
					package url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"math"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/errors"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/net/idna"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						URLNotAbsolute  = "URL is not absolute"
 | 
				
			||||||
 | 
						InvalidURL      = "Invalid URL"
 | 
				
			||||||
 | 
						InvalidBaseURL  = "Invalid base URL"
 | 
				
			||||||
 | 
						InvalidHostname = "Invalid hostname"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						reflectTypeURL = reflect.TypeOf((*nodeURL)(nil))
 | 
				
			||||||
 | 
						reflectTypeInt = reflect.TypeOf(int64(0))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func toURL(r *goja.Runtime, v goja.Value) *nodeURL {
 | 
				
			||||||
 | 
						if v.ExportType() == reflectTypeURL {
 | 
				
			||||||
 | 
							if u := v.Export().(*nodeURL); u != nil {
 | 
				
			||||||
 | 
								return u
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						panic(errors.NewTypeError(r, errors.ErrCodeInvalidThis, `Value of "this" must be of type URL`))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) newInvalidURLError(msg, input string) *goja.Object {
 | 
				
			||||||
 | 
						o := errors.NewTypeError(m.r, "ERR_INVALID_URL", msg)
 | 
				
			||||||
 | 
						o.Set("input", m.r.ToValue(input))
 | 
				
			||||||
 | 
						return o
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) defineURLAccessorProp(p *goja.Object, name string, getter func(*nodeURL) interface{}, setter func(*nodeURL, goja.Value)) {
 | 
				
			||||||
 | 
						var getterVal, setterVal goja.Value
 | 
				
			||||||
 | 
						if getter != nil {
 | 
				
			||||||
 | 
							getterVal = m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
								return m.r.ToValue(getter(toURL(m.r, call.This)))
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if setter != nil {
 | 
				
			||||||
 | 
							setterVal = m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
								setter(toURL(m.r, call.This), call.Argument(0))
 | 
				
			||||||
 | 
								return goja.Undefined()
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						p.DefineAccessorProperty(name, getterVal, setterVal, goja.FLAG_FALSE, goja.FLAG_TRUE)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func valueToURLPort(v goja.Value) (portNum int, empty bool) {
 | 
				
			||||||
 | 
						portNum = -1
 | 
				
			||||||
 | 
						if et := v.ExportType(); et == reflectTypeInt {
 | 
				
			||||||
 | 
							num := v.ToInteger()
 | 
				
			||||||
 | 
							if num < 0 {
 | 
				
			||||||
 | 
								empty = true
 | 
				
			||||||
 | 
							} else if num <= math.MaxUint16 {
 | 
				
			||||||
 | 
								portNum = int(num)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							s := v.String()
 | 
				
			||||||
 | 
							if s == "" {
 | 
				
			||||||
 | 
								return 0, true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							firstDigitIdx := -1
 | 
				
			||||||
 | 
							for i := 0; i < len(s); i++ {
 | 
				
			||||||
 | 
								if c := s[i]; c >= '0' && c <= '9' {
 | 
				
			||||||
 | 
									firstDigitIdx = i
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if firstDigitIdx == -1 {
 | 
				
			||||||
 | 
								return -1, false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if firstDigitIdx > 0 {
 | 
				
			||||||
 | 
								return 0, true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for i := 0; i < len(s); i++ {
 | 
				
			||||||
 | 
								if c := s[i]; c >= '0' && c <= '9' {
 | 
				
			||||||
 | 
									if portNum == -1 {
 | 
				
			||||||
 | 
										portNum = 0
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									portNum = portNum*10 + int(c-'0')
 | 
				
			||||||
 | 
									if portNum > math.MaxUint16 {
 | 
				
			||||||
 | 
										portNum = -1
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isDefaultURLPort(protocol string, port int) bool {
 | 
				
			||||||
 | 
						switch port {
 | 
				
			||||||
 | 
						case 21:
 | 
				
			||||||
 | 
							if protocol == "ftp" {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case 80:
 | 
				
			||||||
 | 
							if protocol == "http" || protocol == "ws" {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case 443:
 | 
				
			||||||
 | 
							if protocol == "https" || protocol == "wss" {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isSpecialProtocol(protocol string) bool {
 | 
				
			||||||
 | 
						switch protocol {
 | 
				
			||||||
 | 
						case "ftp", "file", "http", "https", "ws", "wss":
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func clearURLPort(u *url.URL) {
 | 
				
			||||||
 | 
						u.Host = u.Hostname()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func setURLPort(nu *nodeURL, v goja.Value) {
 | 
				
			||||||
 | 
						u := nu.url
 | 
				
			||||||
 | 
						if u.Scheme == "file" {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						portNum, empty := valueToURLPort(v)
 | 
				
			||||||
 | 
						if empty {
 | 
				
			||||||
 | 
							clearURLPort(u)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if portNum == -1 {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if isDefaultURLPort(u.Scheme, portNum) {
 | 
				
			||||||
 | 
							clearURLPort(u)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							u.Host = u.Hostname() + ":" + strconv.Itoa(portNum)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) parseURL(s string, isBase bool) *url.URL {
 | 
				
			||||||
 | 
						u, err := url.Parse(s)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if isBase {
 | 
				
			||||||
 | 
								panic(m.newInvalidURLError(InvalidBaseURL, s))
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								panic(m.newInvalidURLError(InvalidURL, s))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if isBase && !u.IsAbs() {
 | 
				
			||||||
 | 
							panic(m.newInvalidURLError(URLNotAbsolute, s))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if portStr := u.Port(); portStr != "" {
 | 
				
			||||||
 | 
							if port, err := strconv.Atoi(portStr); err != nil || isDefaultURLPort(u.Scheme, port) {
 | 
				
			||||||
 | 
								u.Host = u.Hostname() // Clear port
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m.fixURL(u)
 | 
				
			||||||
 | 
						return u
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func fixRawQuery(u *url.URL) {
 | 
				
			||||||
 | 
						if u.RawQuery != "" {
 | 
				
			||||||
 | 
							u.RawQuery = escape(u.RawQuery, &tblEscapeURLQuery, false)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) fixURL(u *url.URL) {
 | 
				
			||||||
 | 
						switch u.Scheme {
 | 
				
			||||||
 | 
						case "https", "http", "ftp", "wss", "ws":
 | 
				
			||||||
 | 
							if u.Path == "" {
 | 
				
			||||||
 | 
								u.Path = "/"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							hostname := u.Hostname()
 | 
				
			||||||
 | 
							lh := strings.ToLower(hostname)
 | 
				
			||||||
 | 
							ch, err := idna.Punycode.ToASCII(lh)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(m.newInvalidURLError(InvalidHostname, lh))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if ch != hostname {
 | 
				
			||||||
 | 
								if port := u.Port(); port != "" {
 | 
				
			||||||
 | 
									u.Host = ch + ":" + port
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									u.Host = ch
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fixRawQuery(u)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) createURLPrototype() *goja.Object {
 | 
				
			||||||
 | 
						p := m.r.NewObject()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// host
 | 
				
			||||||
 | 
						m.defineURLAccessorProp(p, "host", func(u *nodeURL) interface{} {
 | 
				
			||||||
 | 
							return u.url.Host
 | 
				
			||||||
 | 
						}, func(u *nodeURL, arg goja.Value) {
 | 
				
			||||||
 | 
							host := arg.String()
 | 
				
			||||||
 | 
							if _, err := url.ParseRequestURI(u.url.Scheme + "://" + host); err == nil {
 | 
				
			||||||
 | 
								u.url.Host = host
 | 
				
			||||||
 | 
								m.fixURL(u.url)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// hash
 | 
				
			||||||
 | 
						m.defineURLAccessorProp(p, "hash", func(u *nodeURL) interface{} {
 | 
				
			||||||
 | 
							if u.url.Fragment != "" {
 | 
				
			||||||
 | 
								return "#" + u.url.EscapedFragment()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}, func(u *nodeURL, arg goja.Value) {
 | 
				
			||||||
 | 
							h := arg.String()
 | 
				
			||||||
 | 
							if len(h) > 0 && h[0] == '#' {
 | 
				
			||||||
 | 
								h = h[1:]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							u.url.Fragment = h
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// hostname
 | 
				
			||||||
 | 
						m.defineURLAccessorProp(p, "hostname", func(u *nodeURL) interface{} {
 | 
				
			||||||
 | 
							return strings.Split(u.url.Host, ":")[0]
 | 
				
			||||||
 | 
						}, func(u *nodeURL, arg goja.Value) {
 | 
				
			||||||
 | 
							h := arg.String()
 | 
				
			||||||
 | 
							if strings.IndexByte(h, ':') >= 0 {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if _, err := url.ParseRequestURI(u.url.Scheme + "://" + h); err == nil {
 | 
				
			||||||
 | 
								if port := u.url.Port(); port != "" {
 | 
				
			||||||
 | 
									u.url.Host = h + ":" + port
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									u.url.Host = h
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								m.fixURL(u.url)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// href
 | 
				
			||||||
 | 
						m.defineURLAccessorProp(p, "href", func(u *nodeURL) interface{} {
 | 
				
			||||||
 | 
							return u.String()
 | 
				
			||||||
 | 
						}, func(u *nodeURL, arg goja.Value) {
 | 
				
			||||||
 | 
							u.url = m.parseURL(arg.String(), true)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// pathname
 | 
				
			||||||
 | 
						m.defineURLAccessorProp(p, "pathname", func(u *nodeURL) interface{} {
 | 
				
			||||||
 | 
							return u.url.EscapedPath()
 | 
				
			||||||
 | 
						}, func(u *nodeURL, arg goja.Value) {
 | 
				
			||||||
 | 
							p := arg.String()
 | 
				
			||||||
 | 
							if _, err := url.Parse(p); err == nil {
 | 
				
			||||||
 | 
								switch u.url.Scheme {
 | 
				
			||||||
 | 
								case "https", "http", "ftp", "ws", "wss":
 | 
				
			||||||
 | 
									if !strings.HasPrefix(p, "/") {
 | 
				
			||||||
 | 
										p = "/" + p
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								u.url.Path = p
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// origin
 | 
				
			||||||
 | 
						m.defineURLAccessorProp(p, "origin", func(u *nodeURL) interface{} {
 | 
				
			||||||
 | 
							return u.url.Scheme + "://" + u.url.Hostname()
 | 
				
			||||||
 | 
						}, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// password
 | 
				
			||||||
 | 
						m.defineURLAccessorProp(p, "password", func(u *nodeURL) interface{} {
 | 
				
			||||||
 | 
							p, _ := u.url.User.Password()
 | 
				
			||||||
 | 
							return p
 | 
				
			||||||
 | 
						}, func(u *nodeURL, arg goja.Value) {
 | 
				
			||||||
 | 
							user := u.url.User
 | 
				
			||||||
 | 
							u.url.User = url.UserPassword(user.Username(), arg.String())
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// username
 | 
				
			||||||
 | 
						m.defineURLAccessorProp(p, "username", func(u *nodeURL) interface{} {
 | 
				
			||||||
 | 
							return u.url.User.Username()
 | 
				
			||||||
 | 
						}, func(u *nodeURL, arg goja.Value) {
 | 
				
			||||||
 | 
							p, has := u.url.User.Password()
 | 
				
			||||||
 | 
							if !has {
 | 
				
			||||||
 | 
								u.url.User = url.User(arg.String())
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								u.url.User = url.UserPassword(arg.String(), p)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// port
 | 
				
			||||||
 | 
						m.defineURLAccessorProp(p, "port", func(u *nodeURL) interface{} {
 | 
				
			||||||
 | 
							return u.url.Port()
 | 
				
			||||||
 | 
						}, func(u *nodeURL, arg goja.Value) {
 | 
				
			||||||
 | 
							setURLPort(u, arg)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// protocol
 | 
				
			||||||
 | 
						m.defineURLAccessorProp(p, "protocol", func(u *nodeURL) interface{} {
 | 
				
			||||||
 | 
							return u.url.Scheme + ":"
 | 
				
			||||||
 | 
						}, func(u *nodeURL, arg goja.Value) {
 | 
				
			||||||
 | 
							s := arg.String()
 | 
				
			||||||
 | 
							pos := strings.IndexByte(s, ':')
 | 
				
			||||||
 | 
							if pos >= 0 {
 | 
				
			||||||
 | 
								s = s[:pos]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							s = strings.ToLower(s)
 | 
				
			||||||
 | 
							if isSpecialProtocol(u.url.Scheme) == isSpecialProtocol(s) {
 | 
				
			||||||
 | 
								if _, err := url.ParseRequestURI(s + "://" + u.url.Host); err == nil {
 | 
				
			||||||
 | 
									u.url.Scheme = s
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Search
 | 
				
			||||||
 | 
						m.defineURLAccessorProp(p, "search", func(u *nodeURL) interface{} {
 | 
				
			||||||
 | 
							u.syncSearchParams()
 | 
				
			||||||
 | 
							if u.url.RawQuery != "" {
 | 
				
			||||||
 | 
								return "?" + u.url.RawQuery
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}, func(u *nodeURL, arg goja.Value) {
 | 
				
			||||||
 | 
							u.url.RawQuery = arg.String()
 | 
				
			||||||
 | 
							fixRawQuery(u.url)
 | 
				
			||||||
 | 
							if u.searchParams != nil {
 | 
				
			||||||
 | 
								u.searchParams = parseSearchQuery(u.url.RawQuery)
 | 
				
			||||||
 | 
								if u.searchParams == nil {
 | 
				
			||||||
 | 
									u.searchParams = make(searchParams, 0)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// search Params
 | 
				
			||||||
 | 
						m.defineURLAccessorProp(p, "searchParams", func(u *nodeURL) interface{} {
 | 
				
			||||||
 | 
							if u.searchParams == nil {
 | 
				
			||||||
 | 
								sp := parseSearchQuery(u.url.RawQuery)
 | 
				
			||||||
 | 
								if sp == nil {
 | 
				
			||||||
 | 
									sp = make(searchParams, 0)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								u.searchParams = sp
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return m.newURLSearchParams((*urlSearchParams)(u))
 | 
				
			||||||
 | 
						}, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("toString", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							u := toURL(m.r, call.This)
 | 
				
			||||||
 | 
							u.syncSearchParams()
 | 
				
			||||||
 | 
							return m.r.ToValue(u.url.String())
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("toJSON", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							u := toURL(m.r, call.This)
 | 
				
			||||||
 | 
							u.syncSearchParams()
 | 
				
			||||||
 | 
							return m.r.ToValue(u.url.String())
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return p
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) createURLConstructor() goja.Value {
 | 
				
			||||||
 | 
						f := m.r.ToValue(func(call goja.ConstructorCall) *goja.Object {
 | 
				
			||||||
 | 
							var u *url.URL
 | 
				
			||||||
 | 
							if baseArg := call.Argument(1); !goja.IsUndefined(baseArg) {
 | 
				
			||||||
 | 
								base := m.parseURL(baseArg.String(), true)
 | 
				
			||||||
 | 
								ref := m.parseURL(call.Argument(0).String(), false)
 | 
				
			||||||
 | 
								u = base.ResolveReference(ref)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								u = m.parseURL(call.Argument(0).String(), true)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							res := m.r.ToValue(&nodeURL{url: u}).(*goja.Object)
 | 
				
			||||||
 | 
							res.SetPrototype(call.This.Prototype())
 | 
				
			||||||
 | 
							return res
 | 
				
			||||||
 | 
						}).(*goja.Object)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						proto := m.createURLPrototype()
 | 
				
			||||||
 | 
						f.Set("prototype", proto)
 | 
				
			||||||
 | 
						proto.DefineDataProperty("constructor", f, goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_FALSE)
 | 
				
			||||||
 | 
						return f
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) domainToASCII(domUnicode string) string {
 | 
				
			||||||
 | 
						res, err := idna.ToASCII(domUnicode)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) domainToUnicode(domASCII string) string {
 | 
				
			||||||
 | 
						res, err := idna.ToUnicode(domASCII)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										122
									
								
								goja_nodejs/url/url_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								goja_nodejs/url/url_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,122 @@
 | 
				
			|||||||
 | 
					package url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						_ "embed"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestURL(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
						new(require.Registry).Enable(vm)
 | 
				
			||||||
 | 
						Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c := vm.Get("URL"); c == nil {
 | 
				
			||||||
 | 
							t.Fatal("URL not found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						script := `const url = new URL("https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash");`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := vm.RunString(script); err != nil {
 | 
				
			||||||
 | 
							t.Fatal("Failed to process url script.", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetters(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
						new(require.Registry).Enable(vm)
 | 
				
			||||||
 | 
						Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c := vm.Get("URL"); c == nil {
 | 
				
			||||||
 | 
							t.Fatal("URL not found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						script := `
 | 
				
			||||||
 | 
							new URL("https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hashed");
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v, err := vm.RunString(script)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal("Failed to process url script.", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						url := v.ToObject(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							prop     string
 | 
				
			||||||
 | 
							expected string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								prop:     "hash",
 | 
				
			||||||
 | 
								expected: "#hashed",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								prop:     "host",
 | 
				
			||||||
 | 
								expected: "sub.example.com:8080",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								prop:     "hostname",
 | 
				
			||||||
 | 
								expected: "sub.example.com",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								prop:     "href",
 | 
				
			||||||
 | 
								expected: "https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hashed",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								prop:     "origin",
 | 
				
			||||||
 | 
								expected: "https://sub.example.com",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								prop:     "password",
 | 
				
			||||||
 | 
								expected: "pass",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								prop:     "username",
 | 
				
			||||||
 | 
								expected: "user",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								prop:     "port",
 | 
				
			||||||
 | 
								expected: "8080",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								prop:     "protocol",
 | 
				
			||||||
 | 
								expected: "https:",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								prop:     "search",
 | 
				
			||||||
 | 
								expected: "?query=string",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, test := range tests {
 | 
				
			||||||
 | 
							v := url.Get(test.prop).String()
 | 
				
			||||||
 | 
							if v != test.expected {
 | 
				
			||||||
 | 
								t.Fatal("failed to match " + test.prop + " property. got: " + v + ", expected: " + test.expected)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:embed testdata/url_test.js
 | 
				
			||||||
 | 
					var urlTest string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestJs(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
						new(require.Registry).Enable(vm)
 | 
				
			||||||
 | 
						Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c := vm.Get("URL"); c == nil {
 | 
				
			||||||
 | 
							t.Fatal("URL not found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Script will throw an error on failed validation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := vm.RunScript("testdata/url_test.js", urlTest)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if ex, ok := err.(*goja.Exception); ok {
 | 
				
			||||||
 | 
								t.Fatal(ex.String())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							t.Fatal("Failed to process url script.", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										390
									
								
								goja_nodejs/url/urlsearchparams.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										390
									
								
								goja_nodejs/url/urlsearchparams.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,390 @@
 | 
				
			|||||||
 | 
					package url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/errors"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						reflectTypeURLSearchParams         = reflect.TypeOf((*urlSearchParams)(nil))
 | 
				
			||||||
 | 
						reflectTypeURLSearchParamsIterator = reflect.TypeOf((*urlSearchParamsIterator)(nil))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newInvalidTupleError(r *goja.Runtime) *goja.Object {
 | 
				
			||||||
 | 
						return errors.NewTypeError(r, "ERR_INVALID_TUPLE", "Each query pair must be an iterable [name, value] tuple")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newMissingArgsError(r *goja.Runtime, msg string) *goja.Object {
 | 
				
			||||||
 | 
						return errors.NewTypeError(r, errors.ErrCodeMissingArgs, msg)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newInvalidArgsError(r *goja.Runtime) *goja.Object {
 | 
				
			||||||
 | 
						return errors.NewTypeError(r, "ERR_INVALID_ARG_TYPE", `The "callback" argument must be of type function.`)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func toUrlSearchParams(r *goja.Runtime, v goja.Value) *urlSearchParams {
 | 
				
			||||||
 | 
						if v.ExportType() == reflectTypeURLSearchParams {
 | 
				
			||||||
 | 
							if u := v.Export().(*urlSearchParams); u != nil {
 | 
				
			||||||
 | 
								return u
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						panic(errors.NewTypeError(r, errors.ErrCodeInvalidThis, `Value of "this" must be of type URLSearchParams`))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) newURLSearchParams(sp *urlSearchParams) *goja.Object {
 | 
				
			||||||
 | 
						v := m.r.ToValue(sp).(*goja.Object)
 | 
				
			||||||
 | 
						v.SetPrototype(m.URLSearchParamsPrototype)
 | 
				
			||||||
 | 
						return v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) createURLSearchParamsConstructor() goja.Value {
 | 
				
			||||||
 | 
						f := m.r.ToValue(func(call goja.ConstructorCall) *goja.Object {
 | 
				
			||||||
 | 
							var sp searchParams
 | 
				
			||||||
 | 
							v := call.Argument(0)
 | 
				
			||||||
 | 
							if o, ok := v.(*goja.Object); ok {
 | 
				
			||||||
 | 
								sp = m.buildParamsFromObject(o)
 | 
				
			||||||
 | 
							} else if !goja.IsUndefined(v) {
 | 
				
			||||||
 | 
								sp = parseSearchQuery(v.String())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return m.newURLSearchParams(&urlSearchParams{searchParams: sp})
 | 
				
			||||||
 | 
						}).(*goja.Object)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m.URLSearchParamsPrototype = m.createURLSearchParamsPrototype()
 | 
				
			||||||
 | 
						f.Set("prototype", m.URLSearchParamsPrototype)
 | 
				
			||||||
 | 
						m.URLSearchParamsPrototype.DefineDataProperty("constructor", f, goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_FALSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return f
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) buildParamsFromObject(o *goja.Object) searchParams {
 | 
				
			||||||
 | 
						var query searchParams
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if o.GetSymbol(goja.SymIterator) != nil {
 | 
				
			||||||
 | 
							return m.buildParamsFromIterable(o)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, k := range o.Keys() {
 | 
				
			||||||
 | 
							val := o.Get(k).String()
 | 
				
			||||||
 | 
							query = append(query, searchParam{name: k, value: val})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return query
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) buildParamsFromIterable(o *goja.Object) searchParams {
 | 
				
			||||||
 | 
						var query searchParams
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m.r.ForOf(o, func(val goja.Value) bool {
 | 
				
			||||||
 | 
							obj := val.ToObject(m.r)
 | 
				
			||||||
 | 
							var name, value string
 | 
				
			||||||
 | 
							i := 0
 | 
				
			||||||
 | 
							// Use ForOf to determine if the object is iterable
 | 
				
			||||||
 | 
							m.r.ForOf(obj, func(val goja.Value) bool {
 | 
				
			||||||
 | 
								if i == 0 {
 | 
				
			||||||
 | 
									name = val.String()
 | 
				
			||||||
 | 
									i++
 | 
				
			||||||
 | 
									return true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if i == 1 {
 | 
				
			||||||
 | 
									value = val.String()
 | 
				
			||||||
 | 
									i++
 | 
				
			||||||
 | 
									return true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// Array isn't a tuple
 | 
				
			||||||
 | 
								panic(newInvalidTupleError(m.r))
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Ensure we have two values
 | 
				
			||||||
 | 
							if i <= 1 {
 | 
				
			||||||
 | 
								panic(newInvalidTupleError(m.r))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							query = append(query, searchParam{
 | 
				
			||||||
 | 
								name:  name,
 | 
				
			||||||
 | 
								value: value,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return query
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) createURLSearchParamsPrototype() *goja.Object {
 | 
				
			||||||
 | 
						p := m.r.NewObject()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("append", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							if len(call.Arguments) < 2 {
 | 
				
			||||||
 | 
								panic(newMissingArgsError(m.r, `The "name" and "value" arguments must be specified`))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							u := toUrlSearchParams(m.r, call.This)
 | 
				
			||||||
 | 
							u.searchParams = append(u.searchParams, searchParam{
 | 
				
			||||||
 | 
								name:  call.Argument(0).String(),
 | 
				
			||||||
 | 
								value: call.Argument(1).String(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							u.markUpdated()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return goja.Undefined()
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("delete", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							u := toUrlSearchParams(m.r, call.This)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(call.Arguments) < 1 {
 | 
				
			||||||
 | 
								panic(newMissingArgsError(m.r, `The "name" argument must be specified`))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							name := call.Argument(0).String()
 | 
				
			||||||
 | 
							isValid := func(v searchParam) bool {
 | 
				
			||||||
 | 
								if len(call.Arguments) == 1 {
 | 
				
			||||||
 | 
									return v.name != name
 | 
				
			||||||
 | 
								} else if v.name == name {
 | 
				
			||||||
 | 
									arg := call.Argument(1)
 | 
				
			||||||
 | 
									if !goja.IsUndefined(arg) && v.value == arg.String() {
 | 
				
			||||||
 | 
										return false
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							j := 0
 | 
				
			||||||
 | 
							for i, v := range u.searchParams {
 | 
				
			||||||
 | 
								if isValid(v) {
 | 
				
			||||||
 | 
									if i != j {
 | 
				
			||||||
 | 
										u.searchParams[j] = v
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									j++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							u.searchParams = u.searchParams[:j]
 | 
				
			||||||
 | 
							u.markUpdated()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return goja.Undefined()
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entries := m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							return m.newURLSearchParamsIterator(toUrlSearchParams(m.r, call.This), urlSearchParamsIteratorEntries)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						p.Set("entries", entries)
 | 
				
			||||||
 | 
						p.DefineDataPropertySymbol(goja.SymIterator, entries, goja.FLAG_TRUE, goja.FLAG_FALSE, goja.FLAG_TRUE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("forEach", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							u := toUrlSearchParams(m.r, call.This)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(call.Arguments) != 1 {
 | 
				
			||||||
 | 
								panic(newInvalidArgsError(m.r))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if fn, ok := goja.AssertFunction(call.Argument(0)); ok {
 | 
				
			||||||
 | 
								for _, pair := range u.searchParams {
 | 
				
			||||||
 | 
									// name, value, searchParams
 | 
				
			||||||
 | 
									_, err := fn(
 | 
				
			||||||
 | 
										nil,
 | 
				
			||||||
 | 
										m.r.ToValue(pair.name),
 | 
				
			||||||
 | 
										m.r.ToValue(pair.value),
 | 
				
			||||||
 | 
										call.This,
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										panic(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								panic(newInvalidArgsError(m.r))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return goja.Undefined()
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("get", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							u := toUrlSearchParams(m.r, call.This)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(call.Arguments) == 0 {
 | 
				
			||||||
 | 
								panic(newMissingArgsError(m.r, `The "name" argument must be specified`))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if val, exists := u.getFirstValue(call.Argument(0).String()); exists {
 | 
				
			||||||
 | 
								return m.r.ToValue(val)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return goja.Null()
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("getAll", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							u := toUrlSearchParams(m.r, call.This)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(call.Arguments) == 0 {
 | 
				
			||||||
 | 
								panic(newMissingArgsError(m.r, `The "name" argument must be specified`))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							vals := u.getValues(call.Argument(0).String())
 | 
				
			||||||
 | 
							return m.r.ToValue(vals)
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("has", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							u := toUrlSearchParams(m.r, call.This)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(call.Arguments) == 0 {
 | 
				
			||||||
 | 
								panic(newMissingArgsError(m.r, `The "name" argument must be specified`))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							name := call.Argument(0).String()
 | 
				
			||||||
 | 
							value := call.Argument(1)
 | 
				
			||||||
 | 
							var res bool
 | 
				
			||||||
 | 
							if goja.IsUndefined(value) {
 | 
				
			||||||
 | 
								res = u.hasName(name)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								res = u.hasValue(name, value.String())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return m.r.ToValue(res)
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("keys", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							return m.newURLSearchParamsIterator(toUrlSearchParams(m.r, call.This), urlSearchParamsIteratorKeys)
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("set", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							u := toUrlSearchParams(m.r, call.This)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(call.Arguments) < 2 {
 | 
				
			||||||
 | 
								panic(newMissingArgsError(m.r, `The "name" and "value" arguments must be specified`))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							name := call.Argument(0).String()
 | 
				
			||||||
 | 
							found := false
 | 
				
			||||||
 | 
							j := 0
 | 
				
			||||||
 | 
							for i, sp := range u.searchParams {
 | 
				
			||||||
 | 
								if sp.name == name {
 | 
				
			||||||
 | 
									if found {
 | 
				
			||||||
 | 
										continue // Remove all values
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									u.searchParams[i].value = call.Argument(1).String()
 | 
				
			||||||
 | 
									found = true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if i != j {
 | 
				
			||||||
 | 
									u.searchParams[j] = sp
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								j++
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !found {
 | 
				
			||||||
 | 
								u.searchParams = append(u.searchParams, searchParam{
 | 
				
			||||||
 | 
									name:  name,
 | 
				
			||||||
 | 
									value: call.Argument(1).String(),
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								u.searchParams = u.searchParams[:j]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							u.markUpdated()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return goja.Undefined()
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("sort", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							u := toUrlSearchParams(m.r, call.This)
 | 
				
			||||||
 | 
							sort.Stable(u.searchParams)
 | 
				
			||||||
 | 
							u.markUpdated()
 | 
				
			||||||
 | 
							return goja.Undefined()
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.DefineAccessorProperty("size", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							u := toUrlSearchParams(m.r, call.This)
 | 
				
			||||||
 | 
							return m.r.ToValue(len(u.searchParams))
 | 
				
			||||||
 | 
						}), nil, goja.FLAG_FALSE, goja.FLAG_TRUE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("toString", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							u := toUrlSearchParams(m.r, call.This)
 | 
				
			||||||
 | 
							str := u.searchParams.Encode()
 | 
				
			||||||
 | 
							return m.r.ToValue(str)
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("values", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							return m.newURLSearchParamsIterator(toUrlSearchParams(m.r, call.This), urlSearchParamsIteratorValues)
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return p
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (sp *urlSearchParams) markUpdated() {
 | 
				
			||||||
 | 
						if sp.url != nil && sp.url.RawQuery != "" {
 | 
				
			||||||
 | 
							sp.url.RawQuery = ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type urlSearchParamsIteratorType int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						urlSearchParamsIteratorKeys urlSearchParamsIteratorType = iota
 | 
				
			||||||
 | 
						urlSearchParamsIteratorValues
 | 
				
			||||||
 | 
						urlSearchParamsIteratorEntries
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type urlSearchParamsIterator struct {
 | 
				
			||||||
 | 
						typ urlSearchParamsIteratorType
 | 
				
			||||||
 | 
						sp  *urlSearchParams
 | 
				
			||||||
 | 
						idx int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func toURLSearchParamsIterator(r *goja.Runtime, v goja.Value) *urlSearchParamsIterator {
 | 
				
			||||||
 | 
						if v.ExportType() == reflectTypeURLSearchParamsIterator {
 | 
				
			||||||
 | 
							if u := v.Export().(*urlSearchParamsIterator); u != nil {
 | 
				
			||||||
 | 
								return u
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						panic(errors.NewTypeError(r, errors.ErrCodeInvalidThis, `Value of "this" must be of type URLSearchParamIterator`))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) getURLSearchParamsIteratorPrototype() *goja.Object {
 | 
				
			||||||
 | 
						if m.URLSearchParamsIteratorPrototype != nil {
 | 
				
			||||||
 | 
							return m.URLSearchParamsIteratorPrototype
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p := m.r.NewObject()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.Set("next", m.r.ToValue(func(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
							it := toURLSearchParamsIterator(m.r, call.This)
 | 
				
			||||||
 | 
							res := m.r.NewObject()
 | 
				
			||||||
 | 
							if it.idx < len(it.sp.searchParams) {
 | 
				
			||||||
 | 
								param := it.sp.searchParams[it.idx]
 | 
				
			||||||
 | 
								switch it.typ {
 | 
				
			||||||
 | 
								case urlSearchParamsIteratorKeys:
 | 
				
			||||||
 | 
									res.Set("value", param.name)
 | 
				
			||||||
 | 
								case urlSearchParamsIteratorValues:
 | 
				
			||||||
 | 
									res.Set("value", param.value)
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									res.Set("value", m.r.NewArray(param.name, param.value))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								res.Set("done", false)
 | 
				
			||||||
 | 
								it.idx++
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								res.Set("value", goja.Undefined())
 | 
				
			||||||
 | 
								res.Set("done", true)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return res
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.DefineDataPropertySymbol(goja.SymToStringTag, m.r.ToValue("URLSearchParams Iterator"), goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_TRUE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m.URLSearchParamsIteratorPrototype = p
 | 
				
			||||||
 | 
						return p
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *urlModule) newURLSearchParamsIterator(sp *urlSearchParams, typ urlSearchParamsIteratorType) goja.Value {
 | 
				
			||||||
 | 
						it := m.r.ToValue(&urlSearchParamsIterator{
 | 
				
			||||||
 | 
							typ: typ,
 | 
				
			||||||
 | 
							sp:  sp,
 | 
				
			||||||
 | 
						}).(*goja.Object)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						it.SetPrototype(m.getURLSearchParamsIteratorPrototype())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return it
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										53
									
								
								goja_nodejs/url/urlsearchparams_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								goja_nodejs/url/urlsearchparams_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					package url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						_ "embed"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/console"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func createVM() *goja.Runtime {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
						new(require.Registry).Enable(vm)
 | 
				
			||||||
 | 
						console.Enable(vm)
 | 
				
			||||||
 | 
						Enable(vm)
 | 
				
			||||||
 | 
						return vm
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestURLSearchParams(t *testing.T) {
 | 
				
			||||||
 | 
						vm := createVM()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c := vm.Get("URLSearchParams"); c == nil {
 | 
				
			||||||
 | 
							t.Fatal("URLSearchParams not found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						script := `const params = new URLSearchParams();`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := vm.RunString(script); err != nil {
 | 
				
			||||||
 | 
							t.Fatal("Failed to process url script.", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:embed testdata/url_search_params.js
 | 
				
			||||||
 | 
					var url_search_params string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestURLSearchParameters(t *testing.T) {
 | 
				
			||||||
 | 
						vm := createVM()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c := vm.Get("URLSearchParams"); c == nil {
 | 
				
			||||||
 | 
							t.Fatal("URLSearchParams not found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Script will throw an error on failed validation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := vm.RunScript("testdata/url_search_params.js", url_search_params)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if ex, ok := err.(*goja.Exception); ok {
 | 
				
			||||||
 | 
								t.Fatal(ex.String())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							t.Fatal("Failed to process url script.", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										104
									
								
								goja_nodejs/util/module.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								goja_nodejs/util/module.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,104 @@
 | 
				
			|||||||
 | 
					package util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ModuleName = "util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Util struct {
 | 
				
			||||||
 | 
						runtime *goja.Runtime
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *Util) format(f rune, val goja.Value, w *bytes.Buffer) bool {
 | 
				
			||||||
 | 
						switch f {
 | 
				
			||||||
 | 
						case 's':
 | 
				
			||||||
 | 
							w.WriteString(val.String())
 | 
				
			||||||
 | 
						case 'd':
 | 
				
			||||||
 | 
							w.WriteString(val.ToNumber().String())
 | 
				
			||||||
 | 
						case 'j':
 | 
				
			||||||
 | 
							if json, ok := u.runtime.Get("JSON").(*goja.Object); ok {
 | 
				
			||||||
 | 
								if stringify, ok := goja.AssertFunction(json.Get("stringify")); ok {
 | 
				
			||||||
 | 
									res, err := stringify(json, val)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										panic(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									w.WriteString(res.String())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case '%':
 | 
				
			||||||
 | 
							w.WriteByte('%')
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							w.WriteByte('%')
 | 
				
			||||||
 | 
							w.WriteRune(f)
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *Util) Format(b *bytes.Buffer, f string, args ...goja.Value) {
 | 
				
			||||||
 | 
						pct := false
 | 
				
			||||||
 | 
						argNum := 0
 | 
				
			||||||
 | 
						for _, chr := range f {
 | 
				
			||||||
 | 
							if pct {
 | 
				
			||||||
 | 
								if argNum < len(args) {
 | 
				
			||||||
 | 
									if u.format(chr, args[argNum], b) {
 | 
				
			||||||
 | 
										argNum++
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									b.WriteByte('%')
 | 
				
			||||||
 | 
									b.WriteRune(chr)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								pct = false
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if chr == '%' {
 | 
				
			||||||
 | 
									pct = true
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									b.WriteRune(chr)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, arg := range args[argNum:] {
 | 
				
			||||||
 | 
							b.WriteByte(' ')
 | 
				
			||||||
 | 
							b.WriteString(arg.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *Util) js_format(call goja.FunctionCall) goja.Value {
 | 
				
			||||||
 | 
						var b bytes.Buffer
 | 
				
			||||||
 | 
						var fmt string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if arg := call.Argument(0); !goja.IsUndefined(arg) {
 | 
				
			||||||
 | 
							fmt = arg.String()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var args []goja.Value
 | 
				
			||||||
 | 
						if len(call.Arguments) > 0 {
 | 
				
			||||||
 | 
							args = call.Arguments[1:]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						u.Format(&b, fmt, args...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return u.runtime.ToValue(b.String())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Require(runtime *goja.Runtime, module *goja.Object) {
 | 
				
			||||||
 | 
						u := &Util{
 | 
				
			||||||
 | 
							runtime: runtime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						obj := module.Get("exports").(*goja.Object)
 | 
				
			||||||
 | 
						obj.Set("format", u.js_format)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func New(runtime *goja.Runtime) *Util {
 | 
				
			||||||
 | 
						return &Util{
 | 
				
			||||||
 | 
							runtime: runtime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						require.RegisterCoreModule(ModuleName, Require)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										74
									
								
								goja_nodejs/util/module_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								goja_nodejs/util/module_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					package util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestUtil_Format(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
						util := New(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var b bytes.Buffer
 | 
				
			||||||
 | 
						util.Format(&b, "Test: %% %д %s %d, %j", vm.ToValue("string"), vm.ToValue(42), vm.NewObject())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if res := b.String(); res != "Test: % %д string 42, {}" {
 | 
				
			||||||
 | 
							t.Fatalf("Unexpected result: '%s'", res)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestUtil_Format_NoArgs(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
						util := New(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var b bytes.Buffer
 | 
				
			||||||
 | 
						util.Format(&b, "Test: %s %d, %j")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if res := b.String(); res != "Test: %s %d, %j" {
 | 
				
			||||||
 | 
							t.Fatalf("Unexpected result: '%s'", res)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestUtil_Format_LessArgs(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
						util := New(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var b bytes.Buffer
 | 
				
			||||||
 | 
						util.Format(&b, "Test: %s %d, %j", vm.ToValue("string"), vm.ToValue(42))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if res := b.String(); res != "Test: string 42, %j" {
 | 
				
			||||||
 | 
							t.Fatalf("Unexpected result: '%s'", res)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestUtil_Format_MoreArgs(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
						util := New(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var b bytes.Buffer
 | 
				
			||||||
 | 
						util.Format(&b, "Test: %s %d, %j", vm.ToValue("string"), vm.ToValue(42), vm.NewObject(), vm.ToValue(42.42))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if res := b.String(); res != "Test: string 42, {} 42.42" {
 | 
				
			||||||
 | 
							t.Fatalf("Unexpected result: '%s'", res)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestJSNoArgs(t *testing.T) {
 | 
				
			||||||
 | 
						vm := goja.New()
 | 
				
			||||||
 | 
						new(require.Registry).Enable(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if util, ok := require.Require(vm, ModuleName).(*goja.Object); ok {
 | 
				
			||||||
 | 
							if format, ok := goja.AssertFunction(util.Get("format")); ok {
 | 
				
			||||||
 | 
								res, err := format(util)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Fatal(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if v := res.Export(); v != "" {
 | 
				
			||||||
 | 
									t.Fatalf("Unexpected result: %v", v)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										4
									
								
								js.go
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								js.go
									
									
									
									
									
								
							@ -1,6 +1,8 @@
 | 
				
			|||||||
package ai
 | 
					package ai
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja_nodejs/require"
 | 
				
			||||||
	"apigo.cc/ai/ai/js"
 | 
						"apigo.cc/ai/ai/js"
 | 
				
			||||||
	"apigo.cc/ai/ai/llm"
 | 
						"apigo.cc/ai/ai/llm"
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
@ -8,8 +10,6 @@ import (
 | 
				
			|||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/dop251/goja"
 | 
					 | 
				
			||||||
	"github.com/dop251/goja_nodejs/require"
 | 
					 | 
				
			||||||
	"github.com/ssgo/u"
 | 
						"github.com/ssgo/u"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										47
									
								
								js/ai.go
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								js/ai.go
									
									
									
									
									
								
							@ -1,8 +1,8 @@
 | 
				
			|||||||
package js
 | 
					package js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
	"apigo.cc/ai/ai/llm"
 | 
						"apigo.cc/ai/ai/llm"
 | 
				
			||||||
	"github.com/dop251/goja"
 | 
					 | 
				
			||||||
	"github.com/ssgo/u"
 | 
						"github.com/ssgo/u"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
@ -24,81 +24,96 @@ type AIGCResult struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func RequireAI(lm llm.LLM) map[string]any {
 | 
					func RequireAI(lm llm.LLM) map[string]any {
 | 
				
			||||||
	return map[string]any{
 | 
						return map[string]any{
 | 
				
			||||||
		"ask": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"ask": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			conf, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
								conf, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
				
			||||||
			result, usage, err := lm.Ask(makeChatMessages(args.Arguments), conf, cb)
 | 
								result, usage, err := lm.Ask(makeChatMessages(args.Arguments), conf, cb)
 | 
				
			||||||
			return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
								return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"fastAsk": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"fastAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
								_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
				
			||||||
			result, usage, err := lm.FastAsk(makeChatMessages(args.Arguments), cb)
 | 
								result, usage, err := lm.FastAsk(makeChatMessages(args.Arguments), cb)
 | 
				
			||||||
			return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
								return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"longAsk": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"longAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
								_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
				
			||||||
			result, usage, err := lm.LongAsk(makeChatMessages(args.Arguments), cb)
 | 
								result, usage, err := lm.LongAsk(makeChatMessages(args.Arguments), cb)
 | 
				
			||||||
			return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
								return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"batterAsk": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"batterAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
								_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
				
			||||||
			result, usage, err := lm.BatterAsk(makeChatMessages(args.Arguments), cb)
 | 
								result, usage, err := lm.BatterAsk(makeChatMessages(args.Arguments), cb)
 | 
				
			||||||
			return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
								return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"bestAsk": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"bestAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
								_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
				
			||||||
			result, usage, err := lm.BestAsk(makeChatMessages(args.Arguments), cb)
 | 
								result, usage, err := lm.BestAsk(makeChatMessages(args.Arguments), cb)
 | 
				
			||||||
			return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
								return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		"multiAsk": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"multiAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
								_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
				
			||||||
			result, usage, err := lm.MultiAsk(makeChatMessages(args.Arguments), cb)
 | 
								result, usage, err := lm.MultiAsk(makeChatMessages(args.Arguments), cb)
 | 
				
			||||||
			return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
								return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"bestMultiAsk": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"bestMultiAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
								_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
				
			||||||
			result, usage, err := lm.BestMultiAsk(makeChatMessages(args.Arguments), cb)
 | 
								result, usage, err := lm.BestMultiAsk(makeChatMessages(args.Arguments), cb)
 | 
				
			||||||
			return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
								return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		"codeInterpreterAsk": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"codeInterpreterAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
								_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
				
			||||||
			result, usage, err := lm.CodeInterpreterAsk(makeChatMessages(args.Arguments), cb)
 | 
								result, usage, err := lm.CodeInterpreterAsk(makeChatMessages(args.Arguments), cb)
 | 
				
			||||||
			return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
								return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"webSearchAsk": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"webSearchAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
								_, cb := getAskArgs(args.This, vm, args.Arguments)
 | 
				
			||||||
			result, usage, err := lm.WebSearchAsk(makeChatMessages(args.Arguments), cb)
 | 
								result, usage, err := lm.WebSearchAsk(makeChatMessages(args.Arguments), cb)
 | 
				
			||||||
			return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
								return vm.ToValue(map[string]any{"tokenUsage": toMap(usage), "result": result, "error": getErrorStr(err)})
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		"makeImage": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"makeImage": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			prompt, conf := getAIGCArgs(args.Arguments)
 | 
								prompt, conf := getAIGCArgs(args.Arguments)
 | 
				
			||||||
			results, err := lm.MakeImage(prompt, conf)
 | 
								results, err := lm.MakeImage(prompt, conf)
 | 
				
			||||||
			return makeAIGCResult(vm, results, nil, err)
 | 
								return makeAIGCResult(vm, results, nil, err)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"fastMakeImage": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"fastMakeImage": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			prompt, conf := getAIGCArgs(args.Arguments)
 | 
								prompt, conf := getAIGCArgs(args.Arguments)
 | 
				
			||||||
			results, err := lm.FastMakeImage(prompt, conf)
 | 
								results, err := lm.FastMakeImage(prompt, conf)
 | 
				
			||||||
			return makeAIGCResult(vm, results, nil, err)
 | 
								return makeAIGCResult(vm, results, nil, err)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"bestMakeImage": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"bestMakeImage": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			prompt, conf := getAIGCArgs(args.Arguments)
 | 
								prompt, conf := getAIGCArgs(args.Arguments)
 | 
				
			||||||
			results, err := lm.BestMakeImage(prompt, conf)
 | 
								results, err := lm.BestMakeImage(prompt, conf)
 | 
				
			||||||
			return makeAIGCResult(vm, results, nil, err)
 | 
								return makeAIGCResult(vm, results, nil, err)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		"makeVideo": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"makeVideo": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			prompt, conf := getAIGCArgs(args.Arguments)
 | 
								prompt, conf := getAIGCArgs(args.Arguments)
 | 
				
			||||||
			results, previews, err := lm.MakeVideo(prompt, conf)
 | 
								results, previews, err := lm.MakeVideo(prompt, conf)
 | 
				
			||||||
			return makeAIGCResult(vm, results, previews, err)
 | 
								return makeAIGCResult(vm, results, previews, err)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"fastMakeVideo": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"fastMakeVideo": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			prompt, conf := getAIGCArgs(args.Arguments)
 | 
								prompt, conf := getAIGCArgs(args.Arguments)
 | 
				
			||||||
			results, previews, err := lm.FastMakeVideo(prompt, conf)
 | 
								results, previews, err := lm.FastMakeVideo(prompt, conf)
 | 
				
			||||||
			return makeAIGCResult(vm, results, previews, err)
 | 
								return makeAIGCResult(vm, results, previews, err)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"bestMakeVideo": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"bestMakeVideo": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			prompt, conf := getAIGCArgs(args.Arguments)
 | 
								prompt, conf := getAIGCArgs(args.Arguments)
 | 
				
			||||||
			results, previews, err := lm.BestMakeVideo(prompt, conf)
 | 
								results, previews, err := lm.BestMakeVideo(prompt, conf)
 | 
				
			||||||
			return makeAIGCResult(vm, results, previews, err)
 | 
								return makeAIGCResult(vm, results, previews, err)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
package js
 | 
					package js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"github.com/dop251/goja"
 | 
					 | 
				
			||||||
	"github.com/ssgo/u"
 | 
						"github.com/ssgo/u"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -25,11 +25,11 @@ func MakeArgs(args *goja.FunctionCall, vm *goja.Runtime) *Args {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (args *Args) Check(num int) goja.Value {
 | 
					func (args *Args) Check(num int) *Args {
 | 
				
			||||||
	if len(args.Arguments) < num {
 | 
						if len(args.Arguments) < num {
 | 
				
			||||||
		return args.VM.NewGoError(errors.New("arguments need " + u.String(num) + ", but given " + u.String(len(args.Arguments))))
 | 
							panic(args.VM.NewGoError(errors.New("arguments need " + u.String(num) + ", but given " + u.String(len(args.Arguments)))))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return args
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (args *Args) Int(index int) int {
 | 
					func (args *Args) Int(index int) int {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
package js
 | 
					package js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/dop251/goja"
 | 
					 | 
				
			||||||
	"github.com/ssgo/u"
 | 
						"github.com/ssgo/u"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										47
									
								
								js/file.go
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								js/file.go
									
									
									
									
									
								
							@ -1,54 +1,43 @@
 | 
				
			|||||||
package js
 | 
					package js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
	"github.com/dop251/goja"
 | 
					 | 
				
			||||||
	"github.com/ssgo/u"
 | 
						"github.com/ssgo/u"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func RequireFile() map[string]any {
 | 
					func RequireFile() map[string]any {
 | 
				
			||||||
	return map[string]any{
 | 
						return map[string]any{
 | 
				
			||||||
		"read": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"read": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			if len(args.Arguments) < 1 {
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
				return vm.NewGoError(errors.New("arguments need 1, but given " + u.String(len(args.Arguments))))
 | 
								if r, err := u.ReadFile(findPath(vm, args.Str(0))); err == nil {
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if r, err := u.ReadFile(findPath(vm, u.String(args.Arguments[0].Export()))); err == nil {
 | 
					 | 
				
			||||||
				return vm.ToValue(r)
 | 
									return vm.ToValue(r)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				panic(vm.NewGoError(err))
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"write": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"write": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			if len(args.Arguments) < 2 {
 | 
								args := MakeArgs(&argsIn, vm).Check(2)
 | 
				
			||||||
				return vm.NewGoError(errors.New("arguments need 2, but given " + u.String(len(args.Arguments))))
 | 
								if err := u.WriteFileBytes(findPath(vm, args.Str(0)), u.Bytes(args.Any(0))); err == nil {
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if err := u.WriteFileBytes(findPath(vm, u.String(args.Arguments[0].Export())), u.Bytes(args.Arguments[0].Export())); err == nil {
 | 
					 | 
				
			||||||
				return nil
 | 
									return nil
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"dir": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"dir": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			if len(args.Arguments) < 1 {
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
				return vm.NewGoError(errors.New("arguments need 1, but given " + u.String(len(args.Arguments))))
 | 
								if r, err := u.ReadDir(findPath(vm, args.Str(0))); err == nil {
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if r, err := u.ReadDir(findPath(vm, u.String(args.Arguments[0].Export()))); err == nil {
 | 
					 | 
				
			||||||
				return vm.ToValue(r)
 | 
									return vm.ToValue(r)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"stat": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"stat": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			if len(args.Arguments) < 1 {
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
				return vm.NewGoError(errors.New("arguments need 1, but given " + u.String(len(args.Arguments))))
 | 
								return vm.ToValue(u.GetFileInfo(findPath(vm, args.Str(0))))
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return vm.ToValue(u.GetFileInfo(findPath(vm, u.String(args.Arguments[0].Export()))))
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"find": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"find": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			if len(args.Arguments) < 1 {
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
				return vm.NewGoError(errors.New("arguments need 1, but given " + u.String(len(args.Arguments))))
 | 
								return vm.ToValue(findPath(vm, args.Str(0)))
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return vm.ToValue(findPath(vm, u.String(args.Arguments[0].Export())))
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										44
									
								
								js/http.go
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								js/http.go
									
									
									
									
									
								
							@ -1,7 +1,7 @@
 | 
				
			|||||||
package js
 | 
					package js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/dop251/goja"
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
	"github.com/ssgo/httpclient"
 | 
						"github.com/ssgo/httpclient"
 | 
				
			||||||
	"github.com/ssgo/u"
 | 
						"github.com/ssgo/u"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -41,7 +41,7 @@ func RequireHTTP() map[string]any {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func makeResult(r *httpclient.Result, vm *goja.Runtime) goja.Value {
 | 
					func makeResult(r *httpclient.Result, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
	if r.Error != nil {
 | 
						if r.Error != nil {
 | 
				
			||||||
		return vm.NewGoError(r.Error)
 | 
							panic(vm.NewGoError(r.Error))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	headers := map[string]string{}
 | 
						headers := map[string]string{}
 | 
				
			||||||
	for k, v := range r.Response.Header {
 | 
						for k, v := range r.Response.Header {
 | 
				
			||||||
@ -57,50 +57,32 @@ func makeResult(r *httpclient.Result, vm *goja.Runtime) goja.Value {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hc *Http) Get(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
					func (hc *Http) Get(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
	args := MakeArgs(&argsIn, vm)
 | 
						args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
	if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return makeResult(hc.client.Get(args.Str(0), args.Map2StrArr(1)...), vm)
 | 
						return makeResult(hc.client.Get(args.Str(0), args.Map2StrArr(1)...), vm)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hc *Http) Head(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
					func (hc *Http) Head(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
	args := MakeArgs(&argsIn, vm)
 | 
						args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
	if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return makeResult(hc.client.Head(args.Str(0), args.Map2StrArr(1)...), vm)
 | 
						return makeResult(hc.client.Head(args.Str(0), args.Map2StrArr(1)...), vm)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hc *Http) Post(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
					func (hc *Http) Post(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
	args := MakeArgs(&argsIn, vm)
 | 
						args := MakeArgs(&argsIn, vm).Check(2)
 | 
				
			||||||
	if err := args.Check(2); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return makeResult(hc.client.Post(args.Str(0), args.Any(1), args.Map2StrArr(2)...), vm)
 | 
						return makeResult(hc.client.Post(args.Str(0), args.Any(1), args.Map2StrArr(2)...), vm)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hc *Http) Put(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
					func (hc *Http) Put(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
	args := MakeArgs(&argsIn, vm)
 | 
						args := MakeArgs(&argsIn, vm).Check(2)
 | 
				
			||||||
	if err := args.Check(2); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return makeResult(hc.client.Put(args.Str(0), args.Any(1), args.Map2StrArr(2)...), vm)
 | 
						return makeResult(hc.client.Put(args.Str(0), args.Any(1), args.Map2StrArr(2)...), vm)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hc *Http) Delete(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
					func (hc *Http) Delete(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
	args := MakeArgs(&argsIn, vm)
 | 
						args := MakeArgs(&argsIn, vm).Check(2)
 | 
				
			||||||
	if err := args.Check(2); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return makeResult(hc.client.Delete(args.Str(0), args.Any(1), args.Map2StrArr(2)...), vm)
 | 
						return makeResult(hc.client.Delete(args.Str(0), args.Any(1), args.Map2StrArr(2)...), vm)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hc *Http) Do(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
					func (hc *Http) Do(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
	args := MakeArgs(&argsIn, vm)
 | 
						args := MakeArgs(&argsIn, vm).Check(3)
 | 
				
			||||||
	if err := args.Check(3); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(argsIn.Arguments) == 3 {
 | 
						if len(argsIn.Arguments) == 3 {
 | 
				
			||||||
		argsIn.Arguments = append(argsIn.Arguments, vm.ToValue(nil))
 | 
							argsIn.Arguments = append(argsIn.Arguments, vm.ToValue(nil))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -122,10 +104,7 @@ func (hc *Http) Do(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hc *Http) Upload(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
					func (hc *Http) Upload(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
	args := MakeArgs(&argsIn, vm)
 | 
						args := MakeArgs(&argsIn, vm).Check(2)
 | 
				
			||||||
	if err := args.Check(2); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	postData := map[string]string{}
 | 
						postData := map[string]string{}
 | 
				
			||||||
	postFiles := map[string]any{}
 | 
						postFiles := map[string]any{}
 | 
				
			||||||
	u.Convert(args.Any(1), &postData)
 | 
						u.Convert(args.Any(1), &postData)
 | 
				
			||||||
@ -137,10 +116,7 @@ func (hc *Http) Upload(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hc *Http) Download(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
					func (hc *Http) Download(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
	args := MakeArgs(&argsIn, vm)
 | 
						args := MakeArgs(&argsIn, vm).Check(2)
 | 
				
			||||||
	if err := args.Check(2); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var r *httpclient.Result
 | 
						var r *httpclient.Result
 | 
				
			||||||
	var callback goja.Callable
 | 
						var callback goja.Callable
 | 
				
			||||||
	if len(argsIn.Arguments) > 2 {
 | 
						if len(argsIn.Arguments) > 2 {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										191
									
								
								js/util.go
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								js/util.go
									
									
									
									
									
								
							@ -1,35 +1,28 @@
 | 
				
			|||||||
package js
 | 
					package js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"apigo.cc/ai/ai/goja"
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"encoding/hex"
 | 
						"encoding/hex"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"github.com/dop251/goja"
 | 
					 | 
				
			||||||
	"github.com/ssgo/u"
 | 
						"github.com/ssgo/u"
 | 
				
			||||||
	"gopkg.in/yaml.v3"
 | 
						"gopkg.in/yaml.v3"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"text/template"
 | 
						"text/template"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func RequireUtil() map[string]any {
 | 
					func RequireUtil() map[string]any {
 | 
				
			||||||
	return map[string]any{
 | 
						return map[string]any{
 | 
				
			||||||
		"json": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"json": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if r, err := json.Marshal(args.Arguments[0].Export()); err == nil {
 | 
								if r, err := json.Marshal(args.Arguments[0].Export()); err == nil {
 | 
				
			||||||
				return vm.ToValue(string(r))
 | 
									return vm.ToValue(string(r))
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"jsonP": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"jsonP": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if r, err := json.Marshal(args.Arguments[0].Export()); err == nil {
 | 
								if r, err := json.Marshal(args.Arguments[0].Export()); err == nil {
 | 
				
			||||||
				r1 := bytes.Buffer{}
 | 
									r1 := bytes.Buffer{}
 | 
				
			||||||
				if err2 := json.Indent(&r1, r, "", "  "); err2 == nil {
 | 
									if err2 := json.Indent(&r1, r, "", "  "); err2 == nil {
 | 
				
			||||||
@ -38,145 +31,131 @@ func RequireUtil() map[string]any {
 | 
				
			|||||||
					return vm.ToValue(string(r))
 | 
										return vm.ToValue(string(r))
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"unJson": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"unJson": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var r any
 | 
								var r any
 | 
				
			||||||
			if err := json.Unmarshal(u.Bytes(args.Arguments[0].Export()), &r); err == nil {
 | 
								if err := json.Unmarshal(u.Bytes(args.Arguments[0].Export()), &r); err == nil {
 | 
				
			||||||
				return vm.ToValue(r)
 | 
									return vm.ToValue(r)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"yaml": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"yaml": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if r, err := yaml.Marshal(args.Arguments[0].Export()); err == nil {
 | 
								if r, err := yaml.Marshal(args.Arguments[0].Export()); err == nil {
 | 
				
			||||||
				return vm.ToValue(string(r))
 | 
									return vm.ToValue(string(r))
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"unYaml": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"unYaml": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var r any
 | 
								var r any
 | 
				
			||||||
			if err := yaml.Unmarshal(u.Bytes(args.Arguments[0].Export()), &r); err == nil {
 | 
								if err := yaml.Unmarshal(u.Bytes(args.Arguments[0].Export()), &r); err == nil {
 | 
				
			||||||
				return vm.ToValue(r)
 | 
									return vm.ToValue(r)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"save": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(2)
 | 
				
			||||||
 | 
								filename := args.Str(0)
 | 
				
			||||||
 | 
								var data []byte
 | 
				
			||||||
 | 
								var err error
 | 
				
			||||||
 | 
								if strings.HasSuffix(filename, ".yml") || strings.HasSuffix(filename, ".yaml") {
 | 
				
			||||||
 | 
									data, err = yaml.Marshal(args.Any(1))
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									data, err = json.Marshal(args.Any(1))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if err == nil {
 | 
				
			||||||
 | 
									if err = u.WriteFileBytes(findPath(vm, filename), data); err != nil {
 | 
				
			||||||
 | 
										panic(vm.NewGoError(err))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"load": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
 | 
								filename := args.Str(0)
 | 
				
			||||||
 | 
								if data, err := u.ReadFileBytes(findPath(vm, filename)); err == nil {
 | 
				
			||||||
 | 
									var r any
 | 
				
			||||||
 | 
									if strings.HasSuffix(filename, ".yml") || strings.HasSuffix(filename, ".yaml") {
 | 
				
			||||||
 | 
										err = yaml.Unmarshal(data, &r)
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										err = json.Unmarshal(data, &r)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if err == nil {
 | 
				
			||||||
 | 
										return vm.ToValue(r)
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										panic(vm.NewGoError(err))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"base64": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"base64": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return vm.ToValue(u.Base64(u.Bytes(args.Arguments[0].Export())))
 | 
								return vm.ToValue(u.Base64(u.Bytes(args.Arguments[0].Export())))
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"unBase64": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"unBase64": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return vm.ToValue(u.UnBase64String(args.Str(0)))
 | 
								return vm.ToValue(u.UnBase64String(args.Str(0)))
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"urlBase64": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"urlBase64": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return vm.ToValue(u.UrlBase64String(args.Str(0)))
 | 
								return vm.ToValue(u.UrlBase64String(args.Str(0)))
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"unUrlBase64": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"unUrlBase64": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return vm.ToValue(u.UnUrlBase64String(args.Str(0)))
 | 
								return vm.ToValue(u.UnUrlBase64String(args.Str(0)))
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"hex": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"hex": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return vm.ToValue(hex.EncodeToString(u.Bytes(args.Arguments[0].Export())))
 | 
								return vm.ToValue(hex.EncodeToString(u.Bytes(args.Arguments[0].Export())))
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"unHex": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"unHex": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if r, err := hex.DecodeString(args.Str(0)); err == nil {
 | 
								if r, err := hex.DecodeString(args.Str(0)); err == nil {
 | 
				
			||||||
				return vm.ToValue(string(r))
 | 
									return vm.ToValue(string(r))
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"aes": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"aes": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(3)
 | 
				
			||||||
			if err := args.Check(3); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if r, err := u.EncryptAesBytes(u.Bytes(args.Arguments[0].Export()), u.Bytes(args.Arguments[1].Export()), u.Bytes(args.Arguments[2].Export())); err == nil {
 | 
								if r, err := u.EncryptAesBytes(u.Bytes(args.Arguments[0].Export()), u.Bytes(args.Arguments[1].Export()), u.Bytes(args.Arguments[2].Export())); err == nil {
 | 
				
			||||||
				return vm.ToValue(string(r))
 | 
									return vm.ToValue(string(r))
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"unAes": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"unAes": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(3)
 | 
				
			||||||
			if err := args.Check(3); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if r, err := u.DecryptAesBytes(u.Bytes(args.Arguments[0].Export()), u.Bytes(args.Arguments[1].Export()), u.Bytes(args.Arguments[2].Export())); err == nil {
 | 
								if r, err := u.DecryptAesBytes(u.Bytes(args.Arguments[0].Export()), u.Bytes(args.Arguments[1].Export()), u.Bytes(args.Arguments[2].Export())); err == nil {
 | 
				
			||||||
				return vm.ToValue(string(r))
 | 
									return vm.ToValue(string(r))
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"gzip": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"gzip": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if r, err := u.Gzip(u.Bytes(args.Arguments[0].Export())); err == nil {
 | 
								if r, err := u.Gzip(u.Bytes(args.Arguments[0].Export())); err == nil {
 | 
				
			||||||
				return vm.ToValue(string(r))
 | 
									return vm.ToValue(string(r))
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"gunzip": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"gunzip": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if r, err := u.Gunzip(u.Bytes(args.Arguments[0].Export())); err == nil {
 | 
								if r, err := u.Gunzip(u.Bytes(args.Arguments[0].Export())); err == nil {
 | 
				
			||||||
				return vm.ToValue(string(r))
 | 
									return vm.ToValue(string(r))
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"id": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"id": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
@ -193,43 +172,23 @@ func RequireUtil() map[string]any {
 | 
				
			|||||||
			return vm.ToValue(u.MakeToken(size))
 | 
								return vm.ToValue(u.MakeToken(size))
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"md5": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"md5": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return vm.ToValue(u.MD5(u.Bytes(args.Arguments[0].Export())))
 | 
								return vm.ToValue(u.MD5(u.Bytes(args.Arguments[0].Export())))
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"sha1": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"sha1": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return vm.ToValue(u.Sha1(u.Bytes(args.Arguments[0].Export())))
 | 
								return vm.ToValue(u.Sha1(u.Bytes(args.Arguments[0].Export())))
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"sha256": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"sha256": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return vm.ToValue(u.Sha256(u.Bytes(args.Arguments[0].Export())))
 | 
								return vm.ToValue(u.Sha256(u.Bytes(args.Arguments[0].Export())))
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"sha512": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"sha512": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return vm.ToValue(u.Sha512(u.Bytes(args.Arguments[0].Export())))
 | 
								return vm.ToValue(u.Sha512(u.Bytes(args.Arguments[0].Export())))
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"tpl": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"tpl": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(2)
 | 
				
			||||||
			if err := args.Check(2); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var functions = map[string]any{}
 | 
								var functions = map[string]any{}
 | 
				
			||||||
			if len(argsIn.Arguments) > 2 {
 | 
								if len(argsIn.Arguments) > 2 {
 | 
				
			||||||
				obj := argsIn.Arguments[2].ToObject(vm)
 | 
									obj := argsIn.Arguments[2].ToObject(vm)
 | 
				
			||||||
@ -256,16 +215,12 @@ func RequireUtil() map[string]any {
 | 
				
			|||||||
				err = tpl.Execute(buf, args.Arguments[1].Export())
 | 
									err = tpl.Execute(buf, args.Arguments[1].Export())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return vm.ToValue(buf.String())
 | 
								return vm.ToValue(buf.String())
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"shell": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
							"shell": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
 | 
				
			||||||
			args := MakeArgs(&argsIn, vm)
 | 
								args := MakeArgs(&argsIn, vm).Check(1)
 | 
				
			||||||
			if err := args.Check(1); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			a := make([]string, len(args.Arguments)-1)
 | 
								a := make([]string, len(args.Arguments)-1)
 | 
				
			||||||
			for i := 1; i < len(args.Arguments); i++ {
 | 
								for i := 1; i < len(args.Arguments); i++ {
 | 
				
			||||||
				a[i-1] = args.Str(i)
 | 
									a[i-1] = args.Str(i)
 | 
				
			||||||
@ -274,7 +229,7 @@ func RequireUtil() map[string]any {
 | 
				
			|||||||
			if r, err := u.RunCommand(args.Str(0), a...); err == nil {
 | 
								if r, err := u.RunCommand(args.Str(0), a...); err == nil {
 | 
				
			||||||
				return vm.ToValue(r)
 | 
									return vm.ToValue(r)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return vm.NewGoError(err)
 | 
									panic(vm.NewGoError(err))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user