Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

node:dgram

ImportsImplementation
node:dgram@bytecodealliance/jco-std/wasi/0.2.x/node/24.x.x/dgram

Application code keeps ordinary Node imports:

import { createSocket } from 'node:dgram';

let socket;
export function start() {
    socket = createSocket('udp4');
    socket.on('message', (message, remote) => {
        socket.send(message, remote.port, remote.address);
    });
    return socket.bindSync({ address: '127.0.0.1', port: 0 }).port;
}
export function stop() { socket.close(); }

Use a world exporting start: func() -> u16 and stop: func(), then build with jco componentize source.js --bundle --backend starlingmonkey -w wit -o app.wasm. Jco installs jco:node/dgram@0.1.0 and the guest-exported jco:node/dgram-callbacks@0.1.0 interface. UDP adds no unrelated WASI imports. The default provider returns a catchable ERR_JCO_DGRAM_ADAPTER_REQUIRED error on capability use; importing, constructing, ref/unref, and closing an unused socket need no host access.

To grant UDP access, transpile for explicit instantiation:

jco transpile app.wasm -o out --instantiation async \
  --async-mode jspi --async-exports '*' \
  --map 'jco:node/dgram@0.1.0=jco:node/dgram@0.1.0'

This mapping preserves the WIT interface name in the instantiation imports. Wire a separate Node provider to each instance:

import { instantiate } from './out/app.js';
import { WASIShim } from '@bytecodealliance/preview2-shim/instantiation';
import { createDgramHost } from '@bytecodealliance/jco-std/wasi/0.2.x/node/24.x.x/dgram/host/node';

const imports = new WASIShim().getImportObject();
let instance;
imports['jco:node/dgram@0.1.0'] = createDgramHost(() => instance.dgramCallbacks);
instance = await instantiate(undefined, imports);
const port = await instance.start();
// Send datagrams to 127.0.0.1:port, then call await instance.stop().

The guest implements Node v24.20.0’s socket state, overloads, Buffer messages, lookup customization, block lists, AbortSignal, events, and disposal. IPv4/IPv6, bind/connect (including their synchronous forms), sends, address queries, broadcast, multicast memberships, buffer options, and ref/unref use the typed UDP provider. Host errors preserve codes, errno, address/port, syscall, and buffer SystemError details. Native descriptors and shared cluster-handle adoption throw ERR_JCO_UNSUPPORTED_NODE_API. The deprecated _createSocketHandle, _handle, _receiving, _bindState, _queue, _reuseAddr, _healthCheck, and _stopReceiving entries immediately throw ERR_JCO_UNSUPPORTED_DEPRECATED_NODE_API; legacy sendto remains functional.

The Node provider requires native bindSync and connectSync (available in Node 24.20.0). It queues datagrams, DNS results, and send completions through the component’s callback exports. Return from exported guest tasks before waiting for these events on the host; awaiting a future UDP event inside an active guest task would require component re-entry. Guest-local microtasks replace Node’s nextTick scheduling, and native async-hooks IDs do not cross the boundary. QuickJS currently traps on host-invoked exported resource methods, so its tests cover the module, validation, and denial; full UDP component tests use StarlingMonkey. Multicast and reuse-port availability depend on the host OS.

The implementation adapts MIT-licensed Node lib/dgram.js and its internal handle/lookup flow at v24.20.0. Provenance and the license remain in the source and emitted JavaScript. Audited unenv 2.0.0-rc.24 dgram is a mock with no-op network methods and fixed addresses/buffer sizes, so Jco uses its own adapter and reuses the already-supported Buffer/EventEmitter cores. Only node:dgram is intercepted; bare dgram is unchanged. Direct jco-std adapters can coexist with bundled Node builtins.