From df4587bc2e47dcee973b19ae3e8d40d042dd52e1 Mon Sep 17 00:00:00 2001 From: Noah Gardner Date: Tue, 29 Sep 2026 19:58:47 -0400 Subject: [PATCH] feat!: ez init's layout, main.bend at the root and modules under src/ The hub describes a package by the first line of its first file by path. With everything under ezhttp/ that was src b64's header. main.bend now sits at the package root, beside LICENSE, and opens with a one-sentence description; every module the entry walks is under src/ (wire's C and JS effects under src/effs/). LAWS.bend, PROOF.bend and ENTRY.bend sit at the root, outside the walk, as in ezjson. ez.toml's entry is main.bend; the bench, the flake's proofs check and the README follow the new paths. The walk: LICENSE, main.bend, src/{auth,b64,body,cache,client,cookie, cors,http,server,url,wire,wirespec}.bend, src/effs/wire.{c,js}. BREAKING CHANGE: ezhttp's internal modules moved from ezhttp/ to src/, and the entry from ezhttp/main.bend to main.bend. Importers of main.bend by the package's hub name are unaffected; importers of any other module update the path, e.g. /http.bend becomes /src/http.bend. Co-Authored-By: Claude Opus 5.5 --- ezhttp/ENTRY.bend => ENTRY.bend | 18 +++++++++--------- ezhttp/LAWS.bend => LAWS.bend | 22 +++++++++++----------- ezhttp/PROOF.bend => PROOF.bend | 8 ++++---- README.md | 18 +++++++++--------- bench/compare.nix | 2 +- bench/default.nix | 5 +++-- bench/main.bend | 14 +++++++------- ez.toml | 2 +- flake.nix | 4 ++-- ezhttp/main.bend => main.bend | 22 ++++++++++++---------- {ezhttp => src}/auth.bend | 2 +- {ezhttp => src}/b64.bend | 2 +- {ezhttp => src}/body.bend | 2 +- {ezhttp => src}/cache.bend | 2 +- {ezhttp => src}/client.bend | 2 +- {ezhttp => src}/cookie.bend | 2 +- {ezhttp => src}/cors.bend | 2 +- {ezhttp => src}/effs/wire.c | 0 {ezhttp => src}/effs/wire.js | 0 {ezhttp => src}/http.bend | 2 +- {ezhttp => src}/json.bend | 2 +- {ezhttp => src}/server.bend | 2 +- {ezhttp => src}/url.bend | 2 +- {ezhttp => src}/wire.bend | 2 +- {ezhttp => src}/wirespec.bend | 2 +- 25 files changed, 72 insertions(+), 69 deletions(-) rename ezhttp/ENTRY.bend => ENTRY.bend (94%) rename ezhttp/LAWS.bend => LAWS.bend (99%) rename ezhttp/PROOF.bend => PROOF.bend (99%) rename ezhttp/main.bend => main.bend (90%) rename {ezhttp => src}/auth.bend (85%) rename {ezhttp => src}/b64.bend (97%) rename {ezhttp => src}/body.bend (94%) rename {ezhttp => src}/cache.bend (98%) rename {ezhttp => src}/client.bend (97%) rename {ezhttp => src}/cookie.bend (99%) rename {ezhttp => src}/cors.bend (99%) rename {ezhttp => src}/effs/wire.c (100%) rename {ezhttp => src}/effs/wire.js (100%) rename {ezhttp => src}/http.bend (99%) rename {ezhttp => src}/json.bend (90%) rename {ezhttp => src}/server.bend (99%) rename {ezhttp => src}/url.bend (98%) rename {ezhttp => src}/wire.bend (91%) rename {ezhttp => src}/wirespec.bend (87%) diff --git a/ezhttp/ENTRY.bend b/ENTRY.bend similarity index 94% rename from ezhttp/ENTRY.bend rename to ENTRY.bend index 4fd7cf2..c1e7d17 100644 --- a/ezhttp/ENTRY.bend +++ b/ENTRY.bend @@ -3,18 +3,18 @@ # # main.bend and client.bend reach the wire effect (user foreign code), so from # bend 2.0.32 no file importing them can print ALL PROOFS CHECK: the verdict -# covers every def of every import. `bend ezhttp/ENTRY.bend` must print +# covers every def of every import. `bend ENTRY.bend` must print # SOME PROOFS FAIL with the foreign-code list as its only error (the wire # effect and the defs that reach it); the flake's proofs check holds it to that. import Base -import ./http.bend as Http -import ./url.bend as Url -import ./body.bend as Body -import ./auth.bend as Auth -import ./cookie.bend as Cookie -import ./cache.bend as Cache -import ./cors.bend as Cors -import ./client.bend as Client +import ./src/http.bend as Http +import ./src/url.bend as Url +import ./src/body.bend as Body +import ./src/auth.bend as Auth +import ./src/cookie.bend as Cookie +import ./src/cache.bend as Cache +import ./src/cors.bend as Cors +import ./src/client.bend as Client import ./main.bend as Ez # LAW: parse_url is Url.parse diff --git a/ezhttp/LAWS.bend b/LAWS.bend similarity index 99% rename from ezhttp/LAWS.bend rename to LAWS.bend index 925b915..94ba4f1 100644 --- a/ezhttp/LAWS.bend +++ b/LAWS.bend @@ -2,18 +2,18 @@ # message framing (RFC 9112), HTTP semantics (RFC 9110), TLS selection # (RFC 2818), Basic and Bearer credentials (RFC 7617, RFC 4648, RFC 6750), # cookies (RFC 6265), caching directives (RFC 9111), and Fetch CORS. -# PROOF.bend fills them. `bend ezhttp/PROOF.bend` is the gate. +# PROOF.bend fills them. `bend PROOF.bend` is the gate. import Base -import ./http.bend as Http -import ./url.bend as Url -import ./body.bend as Body -import ./auth.bend as Auth -import ./wirespec.bend as Spec -import ./b64.bend as B64 -import ./cookie.bend as Cookie -import ./cache.bend as Cache -import ./cors.bend as Cors -import ./json.bend as Json +import ./src/http.bend as Http +import ./src/url.bend as Url +import ./src/body.bend as Body +import ./src/auth.bend as Auth +import ./src/wirespec.bend as Spec +import ./src/b64.bend as B64 +import ./src/cookie.bend as Cookie +import ./src/cache.bend as Cache +import ./src/cors.bend as Cors +import ./src/json.bend as Json import 0x81c67699424929b5c44cd8577e18117f/main.bend as Ezjson # LAW (RFC 3986 §3.3 path-absolute / path-abempty with authority): the path a diff --git a/ezhttp/PROOF.bend b/PROOF.bend similarity index 99% rename from ezhttp/PROOF.bend rename to PROOF.bend index 56801b6..700c22e 100644 --- a/ezhttp/PROOF.bend +++ b/PROOF.bend @@ -1,8 +1,8 @@ -# ezhttp: the proofs. `bend ezhttp/PROOF.bend` is the gate. +# ezhttp: the proofs. `bend PROOF.bend` is the gate. import Base -import ./http.bend as Http -import ./url.bend as Url -import ./body.bend as Body +import ./src/http.bend as Http +import ./src/url.bend as Url +import ./src/body.bend as Body import ./LAWS.bend as Laws # --- Base facts used by the framing proofs --- diff --git a/README.md b/README.md index fd3daae..1b4a89f 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Shared types cover both sides: `Header`, structured `Request` / `Reply` (status or method, headers, body). Bodies are empty, UTF-8 text, or an octet list. The HTTP API does not need JSON: `Request`, `Reply`, and the client `Response` stay text and octets, and `encode` / `decode` take any codec. The -hub package is the HTTP library alone. `ezhttp/json.bend`, the optional +hub package is the HTTP library alone. `src/json.bend`, the optional helper that calls [ezjson](https://github.com/Emerging-Patterns/ezjson), is in this repository but not reachable from `main.bend`, so it is not in the hub package; import ezjson from the hub to read a JSON body (below). @@ -43,7 +43,7 @@ and port 443. ``` import 0x5e4e2a9db839a0214ace6923b04b685b/main.bend as Http -import 0x5e4e2a9db839a0214ace6923b04b685b/client.bend as Client +import 0x5e4e2a9db839a0214ace6923b04b685b/src/client.bend as Client def main() -> IO(Client.Response): Http.http.get("https://example.com/") @@ -65,7 +65,7 @@ in v0 for the server. A HEAD response is written with an empty body. ``` import 0x5e4e2a9db839a0214ace6923b04b685b/main.bend as Http -import 0x5e4e2a9db839a0214ace6923b04b685b/http.bend as Msg +import 0x5e4e2a9db839a0214ace6923b04b685b/src/http.bend as Msg def handle(req: Msg.Request) -> Msg.Reply: match req: @@ -82,9 +82,9 @@ Cookies, `Cache-Control`, and CORS are pure helpers on the same messages. ``` import 0x5e4e2a9db839a0214ace6923b04b685b/main.bend as Http -import 0x5e4e2a9db839a0214ace6923b04b685b/http.bend as Msg -import 0x5e4e2a9db839a0214ace6923b04b685b/cookie.bend as Cookie -import 0x5e4e2a9db839a0214ace6923b04b685b/cors.bend as Cors +import 0x5e4e2a9db839a0214ace6923b04b685b/src/http.bend as Msg +import 0x5e4e2a9db839a0214ace6923b04b685b/src/cookie.bend as Cookie +import 0x5e4e2a9db839a0214ace6923b04b685b/src/cors.bend as Cors def authed() -> Msg.Header: Http.basic("user", "pass") @@ -122,10 +122,10 @@ never pair with `Access-Control-Allow-Origin: *`. ## Compliance -Closed equalities in `ezhttp/LAWS.bend`, proved in `ezhttp/PROOF.bend` -(`bend ezhttp/PROOF.bend` prints `ALL PROOFS CHECK`). The laws that say the +Closed equalities in `LAWS.bend`, proved in `PROOF.bend` +(`bend PROOF.bend` prints `ALL PROOFS CHECK`). The laws that say the entry's re-exports and the client's header helpers equal those pure -definitions are in `ezhttp/ENTRY.bend`. `main.bend` and `client.bend` reach +definitions are in `ENTRY.bend`. `main.bend` and `src/client.bend` reach the wire effect, so bend's verdict on that file is `SOME PROOFS FAIL`, with the list of defs that rely on foreign code as its only error. The laws target: diff --git a/bench/compare.nix b/bench/compare.nix index 29aedaa..52db685 100644 --- a/bench/compare.nix +++ b/bench/compare.nix @@ -8,7 +8,7 @@ # Request→response is timed by one ureq process, not a CLI wrapper. # - Load: hey (outside the timed path) reports RPS and p50/p99. Same payload, # count, and concurrency on both servers. -# - JSON cases use ezhttp/json.bend on the Bend side and serde_json on the +# - JSON cases use src/json.bend on the Bend side and serde_json on the # Rust side. Text and octet bodies are the same fixture bytes. # - Ratio = ezhttp/ref when both timers resolve. If MS stays 0, wall/n is # reported and there is no vs claim. Ratios never fail the check. diff --git a/bench/default.nix b/bench/default.nix index 0dfe7d4..0e6264f 100644 --- a/bench/default.nix +++ b/bench/default.nix @@ -18,14 +18,15 @@ let bendLib = ez.bendLib (self + "/ez.lock.toml"); # Sandbox-safe CC: nixpkgs clang (native ELF). BEND_LIB is the locked ezjson - # tree, so ezhttp/json.bend resolves without a hub publish. + # tree, so src/json.bend resolves without a hub publish. drv = pkgs.stdenv.mkDerivation { pname = "ezhttp-bench-drv"; version = "0.1.0"; dontUnpack = true; nativeBuildInputs = [ bend llvm.clang ]; buildPhase = '' - cp -r ${self}/ezhttp ./ezhttp + cp ${self}/main.bend ./main.bend + cp -r ${self}/src ./src mkdir -p bench cp ${./main.bend} bench/main.bend cp ${./fix.bend} bench/fix.bend diff --git a/bench/main.bend b/bench/main.bend index 20389f2..aa52f92 100644 --- a/bench/main.bend +++ b/bench/main.bend @@ -1,12 +1,12 @@ # Fair HTTP bench driver. Fixtures are loaded before IO.now. Timed loops stay # in this process: GET/POST through ezhttp, and serve / serve_once for the -# server track. JSON cases call ezhttp/json.bend (ezjson). Core HTTP types +# server track. JSON cases call src/json.bend (ezjson). Core HTTP types # stay text and octets. -import ../ezhttp/main.bend as Http -import ../ezhttp/http.bend as Msg -import ../ezhttp/body.bend as Body -import ../ezhttp/client.bend as Client -import ../ezhttp/json.bend as Json +import ../main.bend as Http +import ../src/http.bend as Msg +import ../src/body.bend as Body +import ../src/client.bend as Client +import ../src/json.bend as Json import 0x81c67699424929b5c44cd8577e18117f/src/value.bend as V import ./fix.bend as Fix @@ -413,7 +413,7 @@ def reply.json.of(m: Maybe<&2, V.Json>) -> Msg.Reply: case Some{j}: reply.ok(ctype.json(), json.out(Some{j})) -# POST /json: parse with ezhttp/json.bend and answer with the print +# POST /json: parse with src/json.bend and answer with the print def reply.json(body: String) -> Msg.Reply: reply.json.of(Json.json.parse(body)) diff --git a/ez.toml b/ez.toml index e622bbc..cc89629 100644 --- a/ez.toml +++ b/ez.toml @@ -1,6 +1,6 @@ [package] name = "ezhttp" -entry = "ezhttp/main.bend" +entry = "main.bend" [deps.ezjson] hash = "0x81c67699424929b5c44cd8577e18117f" diff --git a/flake.nix b/flake.nix index ecec31f..9e22284 100644 --- a/flake.nix +++ b/flake.nix @@ -44,7 +44,7 @@ checks.${system} = { # every PROOF.bend on this flake's bend: its first line must be # ALL PROOFS CHECK. ez.mkProofs comes back when ez runs on 2.0.34. - # ezhttp/ENTRY.bend states the laws on main.bend and client.bend, + # ENTRY.bend states the laws on main.bend and client.bend, # which reach the wire effect, so its verdict is SOME PROOFS FAIL; # its only error may be the list of defs relying on foreign code. proofs = pkgs.runCommand "ezhttp-proofs" { @@ -58,7 +58,7 @@ echo "$p: $first" [ "$first" = "ALL PROOFS CHECK" ] || exit 1 done - out_entry=$(cd ezhttp && bend ENTRY.bend 2>&1 || true) + out_entry=$(bend ENTRY.bend 2>&1 || true) echo "$out_entry" | head -n 2 [ "$(echo "$out_entry" | sed -n 1p)" = "SOME PROOFS FAIL" ] || exit 1 echo "$out_entry" | sed -n 2p \ diff --git a/ezhttp/main.bend b/main.bend similarity index 90% rename from ezhttp/main.bend rename to main.bend index 7e3751f..a153169 100644 --- a/ezhttp/main.bend +++ b/main.bend @@ -1,14 +1,16 @@ -# ezhttp: HTTP client and server for Bend 2. Shared message types, BYO JSON -# via encode/decode. TLS for the client stays at the wire/runtime layer. +# ezhttp: HTTP/1.1 client and server for Bend 2, with auth, cookie and CORS helpers. +# +# Shared message types, BYO JSON via encode/decode. TLS for the client stays +# at the wire/runtime layer. import Base -import ./url.bend as Url -import ./http.bend as Http -import ./body.bend as Body -import ./client.bend as Client -import ./server.bend as Server -import ./cookie.bend as Cookie -import ./cache.bend as Cache -import ./cors.bend as Cors +import ./src/url.bend as Url +import ./src/http.bend as Http +import ./src/body.bend as Body +import ./src/client.bend as Client +import ./src/server.bend as Server +import ./src/cookie.bend as Cookie +import ./src/cache.bend as Cache +import ./src/cors.bend as Cors # URL parse re-export def parse_url(url: String) -> Url.Loc: diff --git a/ezhttp/auth.bend b/src/auth.bend similarity index 85% rename from ezhttp/auth.bend rename to src/auth.bend index bcc7c84..e21d433 100644 --- a/ezhttp/auth.bend +++ b/src/auth.bend @@ -1,4 +1,4 @@ -# ezhttp/auth: Authorization header values, pure. Kept apart from client.bend +# ezhttp/src/auth: Authorization header values, pure. Kept apart from client.bend # (which reaches the wire effect) so the proof gate can import it. import Base import ./http.bend as Http diff --git a/ezhttp/b64.bend b/src/b64.bend similarity index 97% rename from ezhttp/b64.bend rename to src/b64.bend index da764ff..9c899aa 100644 --- a/ezhttp/b64.bend +++ b/src/b64.bend @@ -1,4 +1,4 @@ -# ezhttp/b64: Base64 encoding for HTTP credentials (RFC 4648 §4). The +# ezhttp/src/b64: Base64 encoding for HTTP credentials (RFC 4648 §4). The # alphabet is the standard table, with `=` padding. Decoding is not required # for the Authorization helpers. import Base diff --git a/ezhttp/body.bend b/src/body.bend similarity index 94% rename from ezhttp/body.bend rename to src/body.bend index d882f4c..99c55c2 100644 --- a/ezhttp/body.bend +++ b/src/body.bend @@ -1,4 +1,4 @@ -# ezhttp/body: request entity as empty, UTF-8 text, or octet list. Core does +# ezhttp/src/body: request entity as empty, UTF-8 text, or octet list. Core does # not depend on ezjson. Callers plug encode/decode through Bend type # parameters (BYO JSON or any other representation). import Base diff --git a/ezhttp/cache.bend b/src/cache.bend similarity index 98% rename from ezhttp/cache.bend rename to src/cache.bend index d8c6ea6..3f2b904 100644 --- a/ezhttp/cache.bend +++ b/src/cache.bend @@ -1,4 +1,4 @@ -# ezhttp/cache: Cache-Control and a small freshness check (RFC 9111). This is +# ezhttp/src/cache: Cache-Control and a small freshness check (RFC 9111). This is # not a shared cache. Expires is recorded; max-age overrides it for freshness. # IMF-fixdate arithmetic is not evaluated. import Base diff --git a/ezhttp/client.bend b/src/client.bend similarity index 97% rename from ezhttp/client.bend rename to src/client.bend index 45f7d8c..5fe390c 100644 --- a/ezhttp/client.bend +++ b/src/client.bend @@ -1,4 +1,4 @@ -# ezhttp/client: fetch a URL. URL and HTTP parsing joined to the wire effect. +# ezhttp/src/client: fetch a URL. URL and HTTP parsing joined to the wire effect. # Answers a structured Response (status + headers + body), not a hub-style # "0\n…" string contract. import Base diff --git a/ezhttp/cookie.bend b/src/cookie.bend similarity index 99% rename from ezhttp/cookie.bend rename to src/cookie.bend index 2e5b66f..69bc992 100644 --- a/ezhttp/cookie.bend +++ b/src/cookie.bend @@ -1,4 +1,4 @@ -# ezhttp/cookie: Set-Cookie and Cookie (RFC 6265 §§4–5). A thin jar: parse +# ezhttp/src/cookie: Set-Cookie and Cookie (RFC 6265 §§4–5). A thin jar: parse # attributes, serialize the request header, and domain/path/secure matching. # Expires is stored, not evaluated against a clock. SameSite is stored; there # is no browsing context to suppress cross-site sends. diff --git a/ezhttp/cors.bend b/src/cors.bend similarity index 99% rename from ezhttp/cors.bend rename to src/cors.bend index ecebe0b..05ada25 100644 --- a/ezhttp/cors.bend +++ b/src/cors.bend @@ -1,4 +1,4 @@ -# ezhttp/cors: Fetch CORS response headers for a simple request and an OPTIONS +# ezhttp/src/cors: Fetch CORS response headers for a simple request and an OPTIONS # preflight. Origins are reflected, or `*`, and credentials never pair with `*`. # See https://fetch.spec.whatwg.org/#cors-protocol. import Base diff --git a/ezhttp/effs/wire.c b/src/effs/wire.c similarity index 100% rename from ezhttp/effs/wire.c rename to src/effs/wire.c diff --git a/ezhttp/effs/wire.js b/src/effs/wire.js similarity index 100% rename from ezhttp/effs/wire.js rename to src/effs/wire.js diff --git a/ezhttp/http.bend b/src/http.bend similarity index 99% rename from ezhttp/http.bend rename to src/http.bend index 40cf0e0..034505f 100644 --- a/ezhttp/http.bend +++ b/src/http.bend @@ -1,4 +1,4 @@ -# ezhttp/http: HTTP/1.1 message text for client and server — request and +# ezhttp/src/http: HTTP/1.1 message text for client and server — request and # response formatting/parsing — with no sockets. Semantics follow RFC 9110; # message syntax and framing follow RFC 9112. TLS is outside this module. import Base diff --git a/ezhttp/json.bend b/src/json.bend similarity index 90% rename from ezhttp/json.bend rename to src/json.bend index a7e44ee..f376702 100644 --- a/ezhttp/json.bend +++ b/src/json.bend @@ -1,4 +1,4 @@ -# ezhttp/json: optional JSON helpers. HTTP messages stay text and octets. +# ezhttp/src/json: optional JSON helpers. HTTP messages stay text and octets. # This module is the only one that imports ezjson. Request, Reply, and the # client Response do not. import Base diff --git a/ezhttp/server.bend b/src/server.bend similarity index 99% rename from ezhttp/server.bend rename to src/server.bend index 1ad0cf3..8ab6e90 100644 --- a/ezhttp/server.bend +++ b/src/server.bend @@ -1,4 +1,4 @@ -# ezhttp/server: listen, accept, parse one request, write one response. +# ezhttp/src/server: listen, accept, parse one request, write one response. # Uses Base TCP (no TLS in v0). One exchange per connection with # Connection: close. Handler is a pure Request -> Reply function. import Base diff --git a/ezhttp/url.bend b/src/url.bend similarity index 98% rename from ezhttp/url.bend rename to src/url.bend index 95184fb..571d4af 100644 --- a/ezhttp/url.bend +++ b/src/url.bend @@ -1,4 +1,4 @@ -# ezhttp/url: a URL taken apart far enough for an HTTP request. Scheme, host, +# ezhttp/src/url: a URL taken apart far enough for an HTTP request. Scheme, host, # port, and an origin-form path (absolute-path, optional query). Userinfo is # stripped. The fragment is not part of the request-target. # diff --git a/ezhttp/wire.bend b/src/wire.bend similarity index 91% rename from ezhttp/wire.bend rename to src/wire.bend index 15da751..3f8942a 100644 --- a/ezhttp/wire.bend +++ b/src/wire.bend @@ -1,4 +1,4 @@ -# ezhttp/wire: one request out and one response back. TLS and DNS stay in +# ezhttp/src/wire: one request out and one response back. TLS and DNS stay in # the effect (OpenSSL via dlopen); this module does not re-specify TLS. # # Spec: scheme, host, port each on their own line; everything after the third diff --git a/ezhttp/wirespec.bend b/src/wirespec.bend similarity index 87% rename from ezhttp/wirespec.bend rename to src/wirespec.bend index db9ddfb..60d81e3 100644 --- a/ezhttp/wirespec.bend +++ b/src/wirespec.bend @@ -1,4 +1,4 @@ -# ezhttp/wirespec: the text the wire effect reads, pure. Kept apart from +# ezhttp/src/wirespec: the text the wire effect reads, pure. Kept apart from # wire.bend (the effect) so the proof gate can import it. # # Spec: scheme, host, port each on their own line; everything after the third