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
23 changes: 13 additions & 10 deletions experiments/iroh-blobs/host/browser-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,34 @@
// (browser-test.mjs) to await and assert on.
//
// Bundled by browser-test.mjs with `deno bundle --platform browser` into
// dist/browser-entry.js; the page passes the translator-shim URL as the
// `translator` query parameter and the guest component is fetched
// relative to this experiment.
// dist/browser-entry.js; the harness pre-translates the guest with
// translate.ts (embedder-api A4), so the page fetches the component plus
// its envelope and no translator ships to the browser.

import { stats } from "./sockets.ts";
import { bridgeStats } from "./bridge.ts";
import { webrtcStats } from "./webrtc-bridge.ts";
import { guestImports, runGuest } from "../../iroh-relay-ws/host/harness.ts";
import { artifactsFrom, guestImports, runGuest } from "../../iroh-relay-ws/host/harness.ts";

const logEl = document.getElementById("log")!;
const t0 = performance.now();

try {
const params = new URLSearchParams(location.search);
const translatorUrl = params.get("translator");
if (!translatorUrl) throw new Error("missing ?translator=<url> (see browser-test.mjs)");
const fetchBytes = async (url: string): Promise<Uint8Array> => {
const resp = await fetch(url);
if (!resp.ok) throw new Error(`GET ${url}: ${resp.status}`);
return new Uint8Array(await resp.arrayBuffer());
};
const [translator, componentBytes] = await Promise.all([
fetchBytes(translatorUrl),
const fetchText = async (url: string): Promise<string> => {
const resp = await fetch(url);
if (!resp.ok) throw new Error(`GET ${url}: ${resp.status}`);
return resp.text();
};
const [componentBytes, envelope] = await Promise.all([
fetchBytes("../guest/target/wasm32-wasip2/release/iroh-blobs-guest.wasm"),
fetchText("./dist/iroh-blobs-guest.plan.json"),
]);
const artifacts = artifactsFrom(envelope, componentBytes);
console.log(`[driver] loaded in ${(performance.now() - t0).toFixed(1)}ms`);

const env: Record<string, string> = {};
Expand All @@ -39,7 +42,7 @@ try {
if (relay) env.RELAY = relay;

await runGuest(
{ componentBytes, translator },
artifacts,
guestImports({ args: ["iroh-blobs-guest"], env }),
);
const summary =
Expand Down
40 changes: 21 additions & 19 deletions experiments/iroh-blobs/host/browser-test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
// live relay→webrtc migration + blob transfer happened. Companion to
// run.ts (Deno path).
//
// - fetches the pinned deltic translator shim (host-deltic/fetch-translator.ts)
// and bundles the page driver with `deno bundle --platform browser`,
// - serves the repository root over HTTP (the guest and translator fetches
// - pre-translates the guest (translate.ts, embedder-api A4 envelope) and
// bundles the page driver with `deno bundle --platform browser` — the
// page fetches component + envelope; no translator ships to the browser,
// - serves the repository root over HTTP (the guest and envelope fetches
// resolve there, and COOP/COEP headers buy 5us timers for the guest's
// RTT measurements),
// - reuses a running iroh-relay on 127.0.0.1:3340 or starts one,
Expand All @@ -19,13 +20,17 @@ import { readFile } from "node:fs/promises";
import { spawn } from "node:child_process";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { extname, join, normalize, relative } from "node:path";
import { extname, join, normalize } from "node:path";
import { fileURLToPath } from "node:url";
import { chromium } from "playwright";

const HOST_DIR = fileURLToPath(new URL(".", import.meta.url));
const ROOT = fileURLToPath(new URL("../../..", import.meta.url));
const PAGE_PATH = "/experiments/iroh-blobs/host/browser.html";
const GUEST_WASM = join(
HOST_DIR,
"../guest/target/wasm32-wasip2/release/iroh-blobs-guest.wasm",
);
const RELAY_BIN = join(ROOT, ".deps/iroh/target/release/iroh-relay");
const TIMEOUT_MS = 90_000;

Expand All @@ -39,20 +44,19 @@ const MIME = {

const run = promisify(execFile);

/** Fetch (cached) the pinned translator shim; return its URL path under ROOT. */
async function fetchTranslator() {
const { stdout } = await run("deno", [
/** Pre-translate the guest (A4): the page fetches this envelope. */
async function translateGuest() {
await run("deno", [
"run",
"--config",
join(ROOT, "host-deltic/deno.json"),
join(HOST_DIR, "../../iroh-relay-ws/host/deno.json"),
"--frozen",
`--allow-read=${ROOT}`,
`--allow-write=${join(ROOT, "target/deltic")}`,
"--allow-net=github.com,objects.githubusercontent.com,release-assets.githubusercontent.com",
join(ROOT, "host-deltic/fetch-translator.ts"),
]);
const path = stdout.trim();
return `/${relative(ROOT, path).split("\\").join("/")}`;
`--allow-write=${join(HOST_DIR, "dist")}`,
join(HOST_DIR, "../../iroh-relay-ws/host/translate.ts"),
GUEST_WASM,
join(HOST_DIR, "dist/iroh-blobs-guest.plan.json"),
], { cwd: HOST_DIR });
}

async function bundleEntry() {
Expand Down Expand Up @@ -133,14 +137,12 @@ async function runPage(browser, url, lines) {
}

const headed = process.argv.includes("--headed");
console.log("[harness] fetching translator + bundling the page driver");
const translatorPath = await fetchTranslator();
console.log("[harness] translating the guest + bundling the page driver");
await translateGuest();
await bundleEntry();
const relay = await ensureRelay();
const server = await serveRoot();
const url = `http://127.0.0.1:${server.address().port}${PAGE_PATH}?translator=${
encodeURIComponent(translatorPath)
}`;
const url = `http://127.0.0.1:${server.address().port}${PAGE_PATH}`;
console.log(`[harness] serving ${url}`);

const lines = [];
Expand Down
16 changes: 4 additions & 12 deletions experiments/iroh-blobs/host/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
// WebRTC). The guest prints its own results; this driver adds a
// watchdog and the shim/bridge counters.
//
// Runs on stock Deno: run.sh fetches the pinned translator shim and
// exports its path as DELTIC_TRANSLATOR.
// Runs on stock Deno; the translator is `@deltic/translator`'s packaged
// asset, loaded through the module graph.

import { defaultTranslator } from "@deltic/translator";
import { stats } from "./sockets.ts";
import { bridgeStats } from "./bridge.ts";
import { webrtcStats } from "./webrtc-bridge.ts";
Expand All @@ -28,19 +29,10 @@ const watchdog = setTimeout(() => {
Deno.exit(1);
}, WATCHDOG_MS);

const shimPath = Deno.env.get("DELTIC_TRANSLATOR");
if (!shimPath) {
console.error(
"[driver] DELTIC_TRANSLATOR is unset — run this through run.sh, which " +
"fetches the pinned translator shim (host-deltic/fetch-translator.ts).",
);
Deno.exit(2);
}

const t0 = performance.now();
const artifacts = {
componentBytes: await Deno.readFile(GUEST_WASM),
translator: await Deno.readFile(shimPath),
translator: await defaultTranslator(),
};
console.log(`[driver] loaded in ${(performance.now() - t0).toFixed(1)}ms`);

Expand Down
15 changes: 3 additions & 12 deletions experiments/iroh-blobs/run.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#!/usr/bin/env bash
# Runs the iroh-blobs spike end to end: a stock iroh-relay server (from
# .deps/iroh, built by the just recipe), then the deltic host on stock
# Deno driving the wasip2 guest — runtime-linked, no transpile step.
# Deno driving the wasip2 guest — runtime-linked, no transpile step; the
# translator ships inside the pinned @deltic/translator package.
# Reuses an already-running relay on 127.0.0.1:3340; kills only what it
# started.
set -euo pipefail
Expand All @@ -20,14 +21,4 @@ if ! curl -s -m 2 http://127.0.0.1:3340 >/dev/null 2>&1; then
done
fi

# The sha256-pinned translator shim, cached under target/deltic/ (the pin
# and the cache live with host-deltic; the deltic tag in
# ../iroh-relay-ws/host/deno.json — the shared config for every
# experiment — matches it).
shim=$(deno run --config ../../host-deltic/deno.json --frozen \
--allow-read=../.. --allow-write=../../target/deltic \
--allow-net=github.com,objects.githubusercontent.com,release-assets.githubusercontent.com \
../../host-deltic/fetch-translator.ts)

DELTIC_TRANSLATOR="$shim" timeout 120 \
deno run -A --config ../iroh-relay-ws/host/deno.json --frozen host/run.ts
timeout 120 deno run -A --config ../iroh-relay-ws/host/deno.json --frozen host/run.ts
23 changes: 13 additions & 10 deletions experiments/iroh-relay-ws/host/browser-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,42 @@
// (browser-test.mjs) to await and assert on.
//
// Bundled by browser-test.mjs with `deno bundle --platform browser` into
// dist/browser-entry.js; the page passes the translator-shim URL as the
// `translator` query parameter and the guest component is fetched
// relative to this experiment.
// dist/browser-entry.js; the harness pre-translates the guest with
// translate.ts (embedder-api A4), so the page fetches the component plus
// its envelope and no translator ships to the browser.

import { stats } from "./sockets.ts";
import { bridgeStats } from "./bridge.ts";
import { webrtcStats } from "./webrtc-bridge.ts";
import { guestImports, runGuest } from "./harness.ts";
import { artifactsFrom, guestImports, runGuest } from "./harness.ts";

const logEl = document.getElementById("log")!;
const t0 = performance.now();

try {
const params = new URLSearchParams(location.search);
const translatorUrl = params.get("translator");
if (!translatorUrl) throw new Error("missing ?translator=<url> (see browser-test.mjs)");
const fetchBytes = async (url: string): Promise<Uint8Array> => {
const resp = await fetch(url);
if (!resp.ok) throw new Error(`GET ${url}: ${resp.status}`);
return new Uint8Array(await resp.arrayBuffer());
};
const [translator, componentBytes] = await Promise.all([
fetchBytes(translatorUrl),
const fetchText = async (url: string): Promise<string> => {
const resp = await fetch(url);
if (!resp.ok) throw new Error(`GET ${url}: ${resp.status}`);
return resp.text();
};
const [componentBytes, envelope] = await Promise.all([
fetchBytes("../guest/target/wasm32-wasip2/release/iroh-relay-ws-guest.wasm"),
fetchText("./dist/iroh-relay-ws-guest.plan.json"),
]);
const artifacts = artifactsFrom(envelope, componentBytes);
console.log(`[driver] loaded in ${(performance.now() - t0).toFixed(1)}ms`);

const env: Record<string, string> = {};
const rustLog = (globalThis as { RUST_LOG?: string }).RUST_LOG;
if (rustLog) env.RUST_LOG = rustLog;

await runGuest(
{ componentBytes, translator },
artifacts,
guestImports({ args: ["iroh-relay-ws-guest"], env }),
);
const summary =
Expand Down
40 changes: 21 additions & 19 deletions experiments/iroh-relay-ws/host/browser-test.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Playwright harness: runs the spike in a real Chromium and asserts the
// live relay→webrtc migration happened. Companion to run.ts (Deno path).
//
// - fetches the pinned deltic translator shim (host-deltic/fetch-translator.ts)
// and bundles the page driver with `deno bundle --platform browser`,
// - serves the repository root over HTTP (the guest/translator fetches
// - pre-translates the guest (translate.ts, embedder-api A4 envelope) and
// bundles the page driver with `deno bundle --platform browser` — the
// page fetches component + envelope; no translator ships to the browser,
// - serves the repository root over HTTP (the guest/envelope fetches
// resolve there, and COOP/COEP headers buy 5us timers for the guest's
// RTT measurements),
// - reuses a running iroh-relay on 127.0.0.1:3340 or starts one,
Expand All @@ -18,13 +19,17 @@ import { readFile } from "node:fs/promises";
import { spawn } from "node:child_process";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { extname, join, normalize, relative } from "node:path";
import { extname, join, normalize } from "node:path";
import { fileURLToPath } from "node:url";
import { chromium } from "playwright";

const HOST_DIR = fileURLToPath(new URL(".", import.meta.url));
const ROOT = fileURLToPath(new URL("../../..", import.meta.url));
const PAGE_PATH = "/experiments/iroh-relay-ws/host/browser.html";
const GUEST_WASM = join(
HOST_DIR,
"../guest/target/wasm32-wasip2/release/iroh-relay-ws-guest.wasm",
);
const RELAY_BIN = join(ROOT, ".deps/iroh/target/release/iroh-relay");
const TIMEOUT_MS = 90_000;

Expand All @@ -38,20 +43,19 @@ const MIME = {

const run = promisify(execFile);

/** Fetch (cached) the pinned translator shim; return its URL path under ROOT. */
async function fetchTranslator() {
const { stdout } = await run("deno", [
/** Pre-translate the guest (A4): the page fetches this envelope. */
async function translateGuest() {
await run("deno", [
"run",
"--config",
join(ROOT, "host-deltic/deno.json"),
join(HOST_DIR, "deno.json"),
"--frozen",
`--allow-read=${ROOT}`,
`--allow-write=${join(ROOT, "target/deltic")}`,
"--allow-net=github.com,objects.githubusercontent.com,release-assets.githubusercontent.com",
join(ROOT, "host-deltic/fetch-translator.ts"),
]);
const path = stdout.trim();
return `/${relative(ROOT, path).split("\\").join("/")}`;
`--allow-write=${join(HOST_DIR, "dist")}`,
join(HOST_DIR, "translate.ts"),
GUEST_WASM,
join(HOST_DIR, "dist/iroh-relay-ws-guest.plan.json"),
], { cwd: HOST_DIR });
}

async function bundleEntry() {
Expand Down Expand Up @@ -130,14 +134,12 @@ async function runPage(browser, url, lines) {
}

const headed = process.argv.includes("--headed");
console.log("[harness] fetching translator + bundling the page driver");
const translatorPath = await fetchTranslator();
console.log("[harness] translating the guest + bundling the page driver");
await translateGuest();
await bundleEntry();
const relay = await ensureRelay();
const server = await serveRoot();
const url = `http://127.0.0.1:${server.address().port}${PAGE_PATH}?translator=${
encodeURIComponent(translatorPath)
}`;
const url = `http://127.0.0.1:${server.address().port}${PAGE_PATH}`;
console.log(`[harness] serving ${url}`);

const lines = [];
Expand Down
4 changes: 2 additions & 2 deletions experiments/iroh-relay-ws/host/browser.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<!doctype html>
<!-- Browser harness for the iroh-relay-ws spike: the same sockets shim,
bridges, and deltic runtime as the Deno driver, runtime-linked in the
browser (JSPI). Serve from the repository root so the guest and
translator fetches resolve; driven by browser-test.mjs, which bundles
browser (JSPI). Serve from the repository root so the guest and envelope
fetches resolve; driven by browser-test.mjs, which bundles
dist/browser-entry.js first. -->
<html>
<head>
Expand Down
14 changes: 9 additions & 5 deletions experiments/iroh-relay-ws/host/deno.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
{
"//": "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, so the deltic pin below is written once. The tag matches host-deltic/deno.json and host-deltic/fetch-translator.ts's TAG (the pin gate there checks all of them; see host-deltic/README.md, 'The pin'). @deltic/runtime/plan serves the translate CLI's envelope self-check (ping-demo/build.sh). 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 (needs polymorph-websocket >= #44's dom-lib fix).",
"//": "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.",
"nodeModulesDir": "auto",
"minimumDependencyAge": {
"age": "P1D",
"exclude": ["jsr:@deltic/*"]
},
"compilerOptions": {
"lib": ["dom", "dom.iterable", "dom.asynciterable", "deno.ns"]
},
"imports": {
"@deltic/runtime/embedder": "https://raw.githubusercontent.com/lann/deltic/pre-a67ee83/runtime/src/embedder/mod.ts",
"@deltic/runtime/plan": "https://raw.githubusercontent.com/lann/deltic/pre-a67ee83/runtime/src/plan/mod.ts",
"@deltic/runtime/shim": "https://raw.githubusercontent.com/lann/deltic/pre-a67ee83/runtime/src/shim/mod.ts",
"@deltic/wasi-shims": "https://raw.githubusercontent.com/lann/deltic/pre-a67ee83/wasi-shims/src/mod.ts",
"@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",
"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
Loading