DenoDatagramSocket
Deno UDP adapter for effect/socket/DatagramSocket, built on the native
Deno.listenDatagram.
make opens a new Deno.DatagramConn for each reader acquisition,
fromDatagramConn adopts a connection the caller opens, and layer
provides the DatagramSocket service.
Deno.listenDatagram is unstable. Run with --unstable-net, or add "net"
to the "unstable" list in deno.json. Without it, acquiring a reader fails
with a DatagramSocketUnsupportedError, and without --allow-net it fails
with a DatagramSocketOpenError of kind "PermissionDenied".
Compared with the Node and Bun adapters, Deno has no connect, no
source-specific multicast, and broadcast is always on. Received packets are
read into one reused 64 KiB buffer and copied out, so payloads are safe to
retain. On Linux a datagram larger than that buffer is silently truncated.
The reader owns the native socket, so a send-only client must still acquire
a reader, or its writes wait forever. Every write waits for Deno's send
promise, so send errors fail the write that caused them.
writeAll sends sequentially and stops at the first failure. Earlier sends
are not resent; the error carries the failed datagram's address. Send order
does not guarantee arrival order.
On Windows, ICMP connection resets or refusals reported by receive are
passed to onError and reading continues.
Example Echo server
import { DenoDatagramSocket } from "@effect/platform-deno"import { Effect } from "effect"
const echo = Effect.gen(function*() { const socket = yield* DenoDatagramSocket.make({ bind: { port: 9000 } }) const reader = yield* socket.reader const writer = yield* socket.writer while (true) { for (const received of yield* reader.pull) { yield* writer.write({ payload: received.payload, address: received }) } }}).pipe(Effect.scoped)Example Send-only client
import { DenoDatagramSocket } from "@effect/platform-deno"import { Effect } from "effect"
const send = (payload: string) => Effect.gen(function*() { const socket = yield* DenoDatagramSocket.make({ peer: { address: "localhost", port: 9000 } }) // Required even for send-only clients. yield* socket.reader const writer = yield* socket.writer yield* writer.write({ payload }) }).pipe(Effect.scoped)Example Multicast receiver and sender
import { DenoDatagramSocket } from "@effect/platform-deno"import { Effect } from "effect"import { NetAddress } from "effect/net"
const group = NetAddress.ipFromStringUnsafe("239.255.0.1")
const receive = Effect.gen(function*() { if (!NetAddress.isMulticast(group) || !NetAddress.isIpv4Address(group)) return const socket = yield* DenoDatagramSocket.make({ bind: { address: "0.0.0.0", port: 5000 }, reuseAddress: true }) const reader = yield* socket.reader yield* reader.joinMulticast({ group, interface: NetAddress.ipv4Loopback }) return yield* reader.pull}).pipe(Effect.scoped)
const announce = Effect.gen(function*() { const socket = yield* DenoDatagramSocket.make({ // Deno has no egress `multicast.interface` option, so the bind address // picks the interface the group's datagrams leave through bind: { address: "127.0.0.1" }, peer: { address: group, port: 5000 }, multicast: { loopback: true } }) yield* socket.reader const writer = yield* socket.writer yield* writer.write({ payload: "hello" })}).pipe(Effect.scoped)Constructors
Adopts a Deno.DatagramConn.
Details
acquire runs once per reader acquisition, inside the reader's scope, and
the adapter closes the connection when that scope ends. The adapter starts
reading as soon as acquire completes, so nothing else should read from the
connection.
Signature
declare function fromDatagramConn<R>(acquire: Effect<DatagramConn, DatagramSocketError, R>, options: AdoptOptions): Effect<DatagramSocket, never, Exclude<R, Scope>>Creates a DatagramSocket that opens and binds a new Deno.DatagramConn
for each reader acquisition.
Details
Creating the socket never fails. Checking for Deno.listenDatagram,
binding and resolving names happen when a reader is acquired, and fail that
acquisition with a DatagramSocketError. Hostnames in bind and peer are
resolved once per acquisition, so no send waits for DNS.
Invalid receiveBuffer.capacity values cause a defect; use a positive
safe integer.
Signature
declare function make(options: Options): Effect<DatagramSocket>Layers
Models
Options
AdoptOptions
typeOptions for fromDatagramConn. The caller configures the adopted
connection, so only the options that live in JavaScript apply.
Signature
type AdoptOptions = Pick<Options, "peer" | "receiveBuffer" | "onError">Options for make and layer.
Details
bind defaults to 0.0.0.0 (or :: for family: "ipv6") and an
ephemeral port. peer is a default destination and doesn't filter senders;
Deno has no connect.
Family selection, in order: explicit family, a bind IP literal, the
peer address, a bind hostname, then "ipv4". Hostname
lookups use the family selected so far, or prefer IPv4 if none is set.
reuseAddress means SO_REUSEADDR on Linux and SO_REUSEPORT on BSD and
macOS. multicast.loopback loops sent multicast datagrams back to the local
host. Deno has no egress multicast interface setting; the interface of
Reader.joinMulticast is the ingress interface, a different socket option.
Signature
interface Options { readonly bind?: { readonly address?: string | IpAddress; readonly port?: number; }; readonly family?: "ipv4" | "ipv6"; readonly multicast?: { readonly loopback?: boolean; }; readonly onError?: (error: DatagramSocketError) => void; readonly peer?: Endpoint; readonly receiveBuffer?: any; readonly reuseAddress?: boolean;}