From f1b53c1533539aecbf16ad19fd9156deae091f92 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Sun, 20 Sep 2026 03:53:17 -0300 Subject: 9web: multiplexer, HTTP view of the tree, live streams, richer page One upstream 9P connection now serves any number of browser WebSocket sessions and, with --serve, plain 9P clients over TCP or Unix (a 9pserve- style frame remux: tags and fids remapped, Tflush forwarded, reconnect on upstream loss). /fs/ maps HTTP onto the tree: GET file or directory (JSON or HTML), Range, HEAD with 9P headers, PUT (create, truncate, append, trailing slash makes a directory), DELETE, and ?follow=1 or text/event-stream turning a blocking read into server-sent events with Tflush on disconnect. The page gains a lazy tree, stat panel, create, rename, delete, upload and follow mode. --probe embeds 9proc for self-introspection. New mux-test and http-fs-test steps; e2e still passes. Co-Authored-By: Claude Fable 5.1 --- build.zig | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'build.zig') diff --git a/build.zig b/build.zig index 13e54ec..334da9f 100644 --- a/build.zig +++ b/build.zig @@ -36,7 +36,6 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("web/main.zig"), .target = target, .optimize = optimize, - .link_libc = true, .imports = &.{.{ .name = "cloud9", .module = module }}, }) }); bridge.root_module.addAnonymousImport("client.wasm", .{ .root_source_file = wasm.getEmittedBin() }); @@ -58,6 +57,19 @@ pub fn build(b: *std.Build) void { .imports = &.{.{ .name = "cloud9", .module = module }}, }) }); b.step("http-test", "Test HTTP WebSocket transport framing").dependOn(&b.addRunArtifact(http_tests).step); + const mux_mod = b.createModule(.{ + .root_source_file = b.path("web/mux.zig"), + .target = target, + .optimize = optimize, + .imports = &.{.{ .name = "cloud9", .module = module }}, + }); + const mux_tests = b.addTest(.{ .root_module = b.createModule(.{ + .root_source_file = b.path("test/web/mux.zig"), + .target = target, + .optimize = optimize, + .imports = &.{ .{ .name = "cloud9", .module = module }, .{ .name = "mux", .module = mux_mod } }, + }) }); + b.step("mux-test", "Test the 9web multiplexer (shared upstream, fid isolation, reconnect)").dependOn(&b.addRunArtifact(mux_tests).step); const e2e = b.addSystemCommand(&.{ "node", "test/web/e2e.mjs" }); e2e.addArtifactArg(bridge); e2e.addArtifactArg(native_http); @@ -165,6 +177,24 @@ pub fn build(b: *std.Build) void { if (i.debug_test_step) |s| programs_test.dependOn(s); } + // 9web can embed 9proc for self-introspection (--probe). The module is + // linked into 9web only when 9proc is enabled (Linux); otherwise the probe + // code compiles to a stub selected by the have_probe build option. + { + const web_opts = b.addOptions(); + web_opts.addOption(bool, "have_probe", proc != null); + bridge.root_module.addImport("build_options", web_opts.createModule()); + if (proc) |i| bridge.root_module.addImport("9proc", i.module); + } + + // The HTTP /fs mapping suite drives 9web against a 9proc-demo upstream. + if (proc) |i| if (i.demo) |demo| { + const hfs = b.addSystemCommand(&.{ "node", "test/web/http_fs.mjs" }); + hfs.addArtifactArg(demo); + hfs.addArtifactArg(bridge); + b.step("http-fs-test", "Test the HTTP /fs view against a 9proc-demo upstream").dependOn(&hfs.step); + }; + const ns: ?ns_build.Artifacts = if (want_9ns) ns_build.add(b, .{ .target = target, .optimize = optimize, -- cgit v1.3