Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions experiments/iroh-relay-ws/host/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ async function open(socket: UdpSocket, url: string, protocols: string[]): Promis
return;
}
bridgeStats.wsIn++;
const bytes = message.tag === "binary" ? message.val : enc.encode(message.val);
const bytes = message.kind === "binary" ? message.value : enc.encode(message.value);
pushDatagram(socket, frame(TAG_MESSAGE, bytes));
}
} catch (err) {
Expand Down Expand Up @@ -93,7 +93,7 @@ registerBridge(1, (socket: UdpSocket, { data }: OutgoingDatagram) => {
// `slice` detaches from any shared buffer; chain preserves ordering.
const bytes = payload.slice();
conn.sendChain = conn.sendChain
.then(() => conn.ws.send({ tag: "binary", val: bytes }))
.then(() => conn.ws.send({ kind: "binary", value: bytes }))
.catch((err) => {
console.error(`[bridge] ws send failed: ${describeErr(err)}`);
pushDatagram(socket, new Uint8Array([TAG_CLOSED]));
Expand Down
10 changes: 5 additions & 5 deletions experiments/iroh-relay-ws/host/deno.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"//": "MODULE-IDENTITY CONSTRAINT: deltic's wasi-shims and the sibling deltic host modules (.deps/{websocket,webrtc}) import @deltic/runtime/embedder by bare specifier internally; this file maps that specifier ONCE for the whole module graph, so `instanceof WitError` holds across every boundary. This is the ONE deno config for all three experiments (iroh-relay-ws, iroh-blobs, ping-demo): the others pass --config pointing here. deltic arrives as exactly-pinned JSR prereleases (0.1.0-pre.g<shorthash> names one upstream commit; @deltic/translator ships the translator wasm for the SAME commit); the version matches host-deltic/deno.json by repo convention (exam-deltic asserts it; see host-deltic/README.md, 'The pin'). minimumDependencyAge keeps Deno's default supply-chain gate for everything else while letting same-day @deltic prereleases resolve. The npm mappings serve the webrtc module's bare specifiers under Deno; a browser build resolves the RTCPeerConnection global instead. compilerOptions.lib carries dom next to deno.ns so the browser entries (browser-entry.ts, ping-demo's demo.ts/overlay.ts) type-check in the same graph as the Deno drivers.",
"//": "MODULE-IDENTITY CONSTRAINT: deltic's wasi-shims and the sibling deltic host modules (.deps/{websocket,webrtc}) import @deltic/runtime/embedder by bare specifier internally; this file maps that specifier ONCE for the whole module graph, so `instanceof ComponentException` holds across every boundary. This is the ONE deno config for all three experiments (iroh-relay-ws, iroh-blobs, ping-demo): the others pass --config pointing here. deltic arrives as exactly-pinned JSR prereleases (0.1.0-pre.g<shorthash> names one upstream commit; @deltic/translator ships the translator wasm for the SAME commit); the version matches host-deltic/deno.json by repo convention (exam-deltic asserts it; see host-deltic/README.md, 'The pin'). minimumDependencyAge keeps Deno's default supply-chain gate for everything else while letting same-day @deltic prereleases resolve. The npm mappings serve the webrtc module's bare specifiers under Deno; a browser build resolves the RTCPeerConnection global instead. compilerOptions.lib carries dom next to deno.ns so the browser entries (browser-entry.ts, ping-demo's demo.ts/overlay.ts) type-check in the same graph as the Deno drivers.",
"nodeModulesDir": "auto",
"minimumDependencyAge": {
"age": "P1D",
Expand All @@ -9,10 +9,10 @@
"lib": ["dom", "dom.iterable", "dom.asynciterable", "deno.ns"]
},
"imports": {
"@deltic/runtime/embedder": "jsr:@deltic/runtime@0.1.0-pre.ga67ee83/embedder",
"@deltic/runtime/shim": "jsr:@deltic/runtime@0.1.0-pre.ga67ee83/shim",
"@deltic/translator": "jsr:@deltic/translator@0.1.0-pre.ga67ee83",
"@deltic/wasi-shims": "jsr:@deltic/wasi-shims@0.1.0-pre.ga67ee83",
"@deltic/runtime/embedder": "jsr:@deltic/runtime@0.1.0-pre.g078aa15/embedder",
"@deltic/runtime/shim": "jsr:@deltic/runtime@0.1.0-pre.g078aa15/shim",
"@deltic/translator": "jsr:@deltic/translator@0.1.0-pre.g078aa15",
"@deltic/wasi-shims": "jsr:@deltic/wasi-shims@0.1.0-pre.g078aa15",
"node-datachannel": "npm:node-datachannel@0.32.3",
"node-datachannel/polyfill": "npm:node-datachannel@0.32.3/polyfill",
"werift": "npm:werift@0.22.2"
Expand Down
38 changes: 23 additions & 15 deletions experiments/iroh-relay-ws/host/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 9 additions & 8 deletions experiments/iroh-relay-ws/host/errors.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
// Render an error for a log line, unwrapping deltic's branded WIT error
// payloads (the sibling host modules throw `WitError<{tag, val?}>`).
// Render an error for a log line, unwrapping deltic's branded
// `ComponentException<{kind, value?}>` payloads (the sibling host modules
// throw these).

import { WitError } from "@deltic/runtime/embedder";
import { ComponentException } from "@deltic/runtime/embedder";

export function describeErr(err: unknown): string {
if (err instanceof WitError) {
const p = err.payload as { tag?: string; val?: unknown } | undefined;
if (p !== undefined && typeof p === "object" && "tag" in (p as object)) {
return `${p.tag}${p.val === undefined ? "" : `(${String(p.val)})`}`;
if (err instanceof ComponentException) {
const p = err.payload as { kind?: string; value?: unknown } | undefined;
if (p !== undefined && typeof p === "object" && "kind" in (p as object)) {
return `${p.kind}${p.value === undefined ? "" : `(${String(p.value)})`}`;
}
return `WitError(${String(p)})`;
return `ComponentException(${String(p)})`;
}
return err instanceof Error ? `${err.name}: ${err.message}` : String(err);
}
2 changes: 1 addition & 1 deletion experiments/iroh-relay-ws/host/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// deltic's wasi-shims and this package import `@deltic/runtime/embedder`
// by bare specifier; the `deno.json` next to this file maps that
// specifier once for the whole module graph, so there is exactly one
// `WitError` module instance and `instanceof` holds across every
// `ComponentException` module instance and `instanceof` holds across every
// boundary — including the branded errors sockets.ts throws.

import type { ComponentArtifacts } from "@deltic/runtime/embedder";
Expand Down
36 changes: 18 additions & 18 deletions experiments/iroh-relay-ws/host/sockets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
// Environment-portable: standard globals only; works under Deno and in
// browsers.

import { WitError } from "@deltic/runtime/embedder";
import { ComponentException } from "@deltic/runtime/embedder";
import { Pollable } from "@deltic/wasi-shims";

// ---------------------------------------------------------------------------
Expand All @@ -43,8 +43,8 @@ export interface Ipv4SocketAddress {
address: [number, number, number, number];
}
export type IpSocketAddress =
| { tag: "ipv4"; val: Ipv4SocketAddress }
| { tag: "ipv6"; val: { port: number; flowInfo: number; address: number[]; scopeId: number } };
| { kind: "ipv4"; value: Ipv4SocketAddress }
| { kind: "ipv6"; value: { port: number; flowInfo: number; address: number[]; scopeId: number } };

/** `wasi:sockets/udp.incoming-datagram` (record, camelCase fields). */
export interface IncomingDatagram {
Expand Down Expand Up @@ -79,8 +79,8 @@ const theNetwork = new Network();
/** The relay-ws bridge's well-known synthetic address (see the guest's
* datagram-pipe use of iroh-relay's wasi `connect()`). */
export const BRIDGE_ADDR: IpSocketAddress = {
tag: "ipv4",
val: { port: 1, address: [127, 0, 0, 1] },
kind: "ipv4",
value: { port: 1, address: [127, 0, 0, 1] },
};

let nextEphemeralPort = 0xc000;
Expand All @@ -104,7 +104,7 @@ export function registerBridge(port: number, cb: BridgeFn): void {
const addrRoutes = new Map<string, BridgeFn>();

const addrKey = (remoteAddress: IpSocketAddress): string =>
`${(remoteAddress.val.address as number[]).join(".")}:${remoteAddress.val.port}`;
`${(remoteAddress.value.address as number[]).join(".")}:${remoteAddress.value.port}`;

/** Bridge hook: claim guest datagrams to one synthetic address. */
export function registerAddrRoute(
Expand Down Expand Up @@ -168,12 +168,12 @@ export class OutgoingDatagramStream {
send(datagrams: OutgoingDatagram[]): bigint {
for (const d of datagrams) {
stats.datagramsOut++;
const route = d.remoteAddress?.val ? addrRoutes.get(addrKey(d.remoteAddress)) : undefined;
const route = d.remoteAddress?.value ? addrRoutes.get(addrKey(d.remoteAddress)) : undefined;
if (route) {
route(this.#sock, d);
continue;
}
const port = d.remoteAddress?.val?.port;
const port = d.remoteAddress?.value?.port;
const bridge = port === undefined ? undefined : bridges.get(port);
if (bridge) {
bridge(this.#sock, d);
Expand Down Expand Up @@ -223,30 +223,30 @@ export class UdpSocket {
this.#pendingBind = localAddress;
}
finishBind(): void {
if (this.#pendingBind === null) throw new WitError("not-in-progress");
if (this.#pendingBind === null) throw new ComponentException("not-in-progress");
let addr = this.#pendingBind;
this.#pendingBind = null;
if (addr.val.port === 0) {
if (addr.value.port === 0) {
addr = {
tag: addr.tag,
val: { ...addr.val, port: nextEphemeralPort++ },
kind: addr.kind,
value: { ...addr.value, port: nextEphemeralPort++ },
} as IpSocketAddress;
}
this.localAddr = addr;
this.bound = true;
socketsByPort.set(addr.val.port, this);
socketsByPort.set(addr.value.port, this);
}
stream(
_remote?: IpSocketAddress,
): [IncomingDatagramStream, OutgoingDatagramStream] {
return [new IncomingDatagramStream(this), new OutgoingDatagramStream(this)];
}
localAddress(): IpSocketAddress {
if (!this.bound || this.localAddr === null) throw new WitError("invalid-state");
if (!this.bound || this.localAddr === null) throw new ComponentException("invalid-state");
return this.localAddr;
}
remoteAddress(): IpSocketAddress {
throw new WitError("invalid-state");
throw new ComponentException("invalid-state");
}
addressFamily(): AddressFamily {
return this.family;
Expand Down Expand Up @@ -274,7 +274,7 @@ export class UdpSocket {

// TCP + name lookup: linked by the libc baseline, never functional.
const unsupported = (): never => {
throw new WitError("not-supported");
throw new ComponentException("not-supported");
};

export class TcpSocket {
Expand Down Expand Up @@ -317,7 +317,7 @@ export class TcpSocket {

export class ResolveAddressStream {
resolveNextAddress(): never {
throw new WitError("permanent-resolver-failure");
throw new ComponentException("permanent-resolver-failure");
}
subscribe(): Pollable {
return ready();
Expand Down Expand Up @@ -352,7 +352,7 @@ export function syntheticNetImports(): Record<string, unknown> {
"wasi:sockets/ip-name-lookup@0.2": {
ResolveAddressStream,
resolveAddresses: (): never => {
throw new WitError("permanent-resolver-failure");
throw new ComponentException("permanent-resolver-failure");
},
},
};
Expand Down
12 changes: 6 additions & 6 deletions experiments/iroh-relay-ws/host/webrtc-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ import {
import { describeErr } from "./errors.ts";

const WEBRTC_FROM: IpSocketAddress = {
tag: "ipv4",
val: { port: 2, address: [127, 0, 0, 1] },
kind: "ipv4",
value: { port: 2, address: [127, 0, 0, 1] },
};
const TAG_REGISTER = 0x00;
const TAG_ASSIGNED = 0x01;
Expand Down Expand Up @@ -89,7 +89,7 @@ const t0 = Date.now();
const ts = (): string => `t+${Date.now() - t0}ms`;

function synFrom(peer: Peer): IpSocketAddress {
return { tag: "ipv4", val: { port: SYN_PORT, address: peer.synAddr } };
return { kind: "ipv4", value: { port: SYN_PORT, address: peer.synAddr } };
}

/** Forward one raw datagram from `srcHex` toward `dstHex`, buffering while
Expand Down Expand Up @@ -143,7 +143,7 @@ registerBridge(2, (socket: UdpSocket, { data }: OutgoingDatagram) => {
registerAddrRoute(peer.synAddr, SYN_PORT, (senderSocket, d) => {
let srcHex = senderSocket.overlayOwner;
if (!srcHex) {
const port = senderSocket.localAddress().val.port;
const port = senderSocket.localAddress().value.port;
for (const [otherHex, other] of peers) {
if (other.udpPort === port) {
srcHex = senderSocket.overlayOwner = otherHex;
Expand Down Expand Up @@ -218,7 +218,7 @@ function pumpChannel(channel: DataChannel, ownerHex: string, remoteHex: string):
return;
}
webrtcStats.in++;
const bytes = message.tag === "binary" ? message.val : new TextEncoder().encode(message.val);
const bytes = message.kind === "binary" ? message.value : new TextEncoder().encode(message.value);
const owner = peers.get(ownerHex);
const remote = peers.get(remoteHex);
if (!owner || !remote) continue;
Expand Down Expand Up @@ -269,7 +269,7 @@ async function establish(key: string, link: Link, a: string, b: string): Promise
link.send = (fromHex, bytes) => {
const channel = chans[fromHex];
sendChains[fromHex] = sendChains[fromHex]
.then(() => channel.send({ tag: "binary", val: bytes }))
.then(() => channel.send({ kind: "binary", value: bytes }))
.catch((err) => console.error(`[webrtc-bridge] send failed: ${describeErr(err)}`));
};
link.state = "open";
Expand Down
2 changes: 1 addition & 1 deletion experiments/ping-demo/web/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function setStatus(state: string, text: string): void {
let guestSocket: any = null;
const encoder = new TextEncoder();
const decoder = new TextDecoder();
const GUEST_FROM = { tag: "ipv4" as const, val: { port: 3, address: [127, 0, 0, 1] as [number, number, number, number] } };
const GUEST_FROM = { kind: "ipv4" as const, value: { port: 3, address: [127, 0, 0, 1] as [number, number, number, number] } };

function sendToGuest(obj: unknown): void {
if (!guestSocket) return;
Expand Down
10 changes: 5 additions & 5 deletions experiments/ping-demo/web/overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
} from "../../iroh-relay-ws/host/sockets.ts";
import { describeErr } from "../../iroh-relay-ws/host/errors.ts";

const CONTROL_FROM: IpSocketAddress = { tag: "ipv4", val: { port: 2, address: [127, 0, 0, 1] } };
const CONTROL_FROM: IpSocketAddress = { kind: "ipv4", value: { port: 2, address: [127, 0, 0, 1] } };
const TAG_REGISTER = 0x00;
const TAG_ASSIGNED = 0x01;
const TAG_READY = 0x02;
Expand Down Expand Up @@ -105,7 +105,7 @@ export function beginUpgrade(
{ remoteIdHex, initiator, sendSignal, onStatus = () => {} }: UpgradeOptions,
): Upgrade {
const remote = synAddr(remoteIdHex);
const remoteFrom: IpSocketAddress = { tag: "ipv4", val: remote };
const remoteFrom: IpSocketAddress = { kind: "ipv4", value: remote };

const config = new PeerConnectionConfig();
config.setIceServers(STUN);
Expand All @@ -126,7 +126,7 @@ export function beginUpgrade(
if (channel) {
overlayStats.out++;
sendChain = sendChain
.then(() => channel!.send({ tag: "binary", val: bytes }))
.then(() => channel!.send({ kind: "binary", value: bytes }))
.catch((err) => console.error(`[overlay] send failed: ${describeErr(err)}`));
} else if (backlog.length < 64) {
backlog.push(bytes);
Expand All @@ -147,7 +147,7 @@ export function beginUpgrade(
}
overlayStats.in++;
const bytes =
message.tag === "binary" ? message.val : new TextEncoder().encode(message.val);
message.kind === "binary" ? message.value : new TextEncoder().encode(message.value);
const sock = self_ && socketByLocalPort(self_.udpPort);
if (sock) pushDatagram(sock, bytes, remoteFrom);
}
Expand Down Expand Up @@ -180,7 +180,7 @@ export function beginUpgrade(
for (const bytes of backlog.splice(0)) {
overlayStats.out++;
sendChain = sendChain
.then(() => channel!.send({ tag: "binary", val: bytes }))
.then(() => channel!.send({ kind: "binary", value: bytes }))
.catch(() => {});
}
// Readiness gates the guest's add_external_addr.
Expand Down
4 changes: 2 additions & 2 deletions host-deltic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ module under its own package-shaped `deno.json` (name + exports)
resolves its bare specifiers against THAT config — before the `.deps`
pins converged on JSR-consuming sibling revisions, the webcrypto module
silently rode a raw pinned-tag embedder while everything else used the
JSR one, and `instanceof WitError` did not hold across its boundary.
JSR one, and `instanceof ComponentException` did not hold across its boundary.
Identity therefore rests on every config in the graph — this one and
each pinned sibling's — naming the SAME `jsr:@deltic/*` version, so the
resolver dedupes to one `WitError`/`Stream` module instance. Two gates
resolver dedupes to one `ComponentException`/`Stream` module instance. Two gates
in `just exam-deltic` keep it true: the pin grep (this repo's configs
agree) and `scripts/deltic-identity-gate.ts` (the RESOLVED run-endpoint
graph carries exactly one `@deltic/runtime` and no raw URLs). Bumping a
Expand Down
Loading
Loading