WIP: rebase for 2026-05-18 #7

Draft
codinget wants to merge 234 commits from rebase/2026-05-18 into webnet
Showing only changes of commit 8357137a59 - Show all commits
+71 -4
View File
@@ -16,6 +16,7 @@ import (
"crypto/x509"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"log"
"math/rand/v2"
@@ -595,11 +596,29 @@ func (i *jsIPN) dial(network, addr string) js.Value {
func (i *jsIPN) listen(network, addr string) js.Value {
return makePromise(func() (any, error) {
pc, err := i.ns.ListenPacket(network, addr)
if err != nil {
return nil, err
switch network {
case "tcp", "tcp4", "tcp6":
// netstack.ListenTCP only accepts tcp4/tcp6; bare "tcp"
// defaults to IPv4 to match net.Listen's typical behavior
// when given an unspecified address.
n := network
if n == "tcp" {
n = "tcp4"
}
ln, err := i.ns.ListenTCP(n, addr)
if err != nil {
return nil, err
}
return wrapTCPListener(ln), nil
case "udp", "udp4", "udp6":
pc, err := i.ns.ListenPacket(network, addr)
if err != nil {
return nil, err
}
return wrapPacketConn(pc), nil
default:
return nil, fmt.Errorf("unsupported network %q", network)
}
return wrapPacketConn(pc), nil
})
}
@@ -717,6 +736,54 @@ func wrapConn(conn net.Conn) map[string]any {
}
}
// wrapTCPListener exposes a net.Listener to JavaScript as an object with
// accept/close/addr methods plus a Symbol.asyncIterator implementation, so
// callers can write `for await (const conn of listener)`.
func wrapTCPListener(ln net.Listener) js.Value {
obj := js.Global().Get("Object").New()
obj.Set("accept", js.FuncOf(func(this js.Value, args []js.Value) any {
return makePromise(func() (any, error) {
conn, err := ln.Accept()
if err != nil {
return nil, err
}
return wrapConn(conn), nil
})
}))
obj.Set("close", js.FuncOf(func(this js.Value, args []js.Value) any {
return ln.Close() != nil
}))
obj.Set("addr", js.FuncOf(func(this js.Value, args []js.Value) any {
return ln.Addr().String()
}))
asyncIterSym := js.Global().Get("Symbol").Get("asyncIterator")
iterFactory := js.FuncOf(func(this js.Value, args []js.Value) any {
iter := js.Global().Get("Object").New()
iter.Set("next", js.FuncOf(func(this js.Value, args []js.Value) any {
return makePromise(func() (any, error) {
conn, err := ln.Accept()
if err != nil {
if errors.Is(err, net.ErrClosed) {
return map[string]any{
"value": js.Undefined(),
"done": true,
}, nil
}
return nil, err
}
return map[string]any{
"value": wrapConn(conn),
"done": false,
}, nil
})
}))
return iter
})
js.Global().Get("Reflect").Call("set", obj, asyncIterSym, iterFactory)
return obj
}
// wrapPacketConn exposes a net.PacketConn to JavaScript with binary (Uint8Array) I/O.
func wrapPacketConn(pc net.PacketConn) map[string]any {
return map[string]any{