ai_old/goja_nodejs/url/urlsearchparams.go
Star 767d87ac3e update throw exception
add util.load and util.save
2024-09-24 13:34:32 +08:00

391 lines
9.7 KiB
Go

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
}