fix(ext/node): support ChildProcess.send with net.Server handles (#34948)
## Summary
`subprocess.send('server', server)` from `node:child_process`, where
`server` is an unlistened `net.createServer()`, threw:
```
error: Uncaught (in promise) Error: Not implemented: ChildProcess.send with non-TCP net.Server handle
```
An unlistened `net.Server` has a `null` underlying handle, and
`getIpcHandleInfo` only accepted TCP handles. This makes
`ChildProcess.send` with `net.Server` / `net.Socket` handles work in the
cases Node supports.
## Changes
- **Null handle → plain message.** An unlistened `net.Server` (or
detached `net.Socket`) has a null underlying handle. `getIpcHandleInfo`
now returns `null` in that case and the message is delivered without a
handle, matching Node's `if (!handle) message = message.msg`. This is
the exact case in the issue's repro.
- **Non-TCP (Pipe / unix-socket) handles.** `net.Server` and
`net.Socket` handles backed by a `Pipe` are now transferable. The IPC
message records `nativeKind` (`"tcp"` | `"pipe"`) so the receiver
reconstructs the correct wrap type.
- **`net.Server.prototype.listen` Pipe branch.** A `Pipe` wrap exposes
an `fd` getter, so without an explicit handle branch `listen(pipe)` fell
into the `options.fd` path and re-opened the already-owned fd, failing
with `EEXIST`.
- **`uv_pipe_open_listener`** (mirrors the existing
`uv_tcp_open_listener`). A pipe opened from an inherited *listening* fd
must not eagerly create an `AsyncFd`; otherwise `uv_pipe_listen`'s
`UnixListener` registration hits `epoll_ctl(EPOLL_CTL_ADD)` `EEXIST` on
the same fd. `PipeWrap::open` dispatches to it for server pipes.
## Tests
Adds `tests/specs/node/child_process_ipc_handle/`:
- `unlistened_server_main.mjs` — the issue's repro (send an unlistened
server; child receives the message with no handle).
- `pipe_server_main.mjs` — transfer a listening unix-socket server;
child reconstructs a working `net.Server`.
Both run under `json` and `advanced` serialization. Full suite (10
tests) passes:
`cargo test --test specs -- node::child_process_ipc_handle`.
Note: a full connect round-trip over a *transferred unix-socket* server
isn't asserted because `closeAfterSend` unlinks the socket file on the
parent's `server.close()`, which is inherent to unix sockets (TCP keeps
working because the bound port stays valid while the child's dup'd fd
listens).
Closes #34921
Closes denoland/divybot#502
Co-authored-by: divybot <divybot@users.noreply.github.com>
Co-authored-by: Divy Srivastava <me@littledivy.com>