web: Middleware, Next, WebApp, the web { } builder and app.answer (step 2 of #94) #776
Labels
No labels
loop
queue/failed
queue/queued
ready
status/parked
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
torbscript/language#776
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Step 2 of the epic #94 (WEB slice 2,
std/webcore). Step 1 (#575, PR #578) is landed onmain:std/webhasWebError,Handler((request: Request) => Task<Result<Response, WebError>>) and the request and response helpers. This step adds the application: the middleware chain, theweb { }builder, the adapter tostd/http's handler andapp.answer(request). NoWebTestyet (step 3), no example (step 4).Source
Next, the error pages,answer.std/httpHandler,Server(std/http/src/server.trb),docs/language/configuration/builders.mdfor the builder shape.assetsnot in the vocabulary yet and the fallback forNext).Design
trait Middleware { fn around(request: Request, next: Next): Task<Result<Response, WebError>> }andtype Next { layers: List<Middleware>, endpoint: Handler, fn run(request) }as in WEB.md section 3.3:runcalls the first layer with aNextover the rest, or the endpoint when none is left. A middleware that answers without callingnextrefuses the request; one that changes the request builds a newRequestfrom the parts of the old one and takes its body over (Request,ResponseandBodyare shared types that own a one-shot stream, see the "Built" note in WEB.md 3.4).WebAppis the builder receiver with exactly three members in this step:wrap(layer: Middleware)(layers run in the order written, the first outermost),handle(handler: Handler),errors(render: (WebError) => Response); noassets(#100).web { }follows the builder rules ofbuilders.md; a block withouthandleis a compile-time or startup failure with a clear message, a secondhandleis refused the same way. Name choice stays inside the receiver-before-module rule (probe 7); the receiver has no other members.errorsis optional; the default rendersWebError.response()of step 1. The hook sees the status and public message only, never the cause (it is logged by the adapter, never shown, WEB.md 3.8).app.answer(request): Task<Result<Response, WebError>>runs layers, handler and, on aFail, theerrorsrenderer, so for a caller it answersOk(page)for everyWebError; it fails only when the renderer itself fails. (The WEB.md 3.9 sample matches both arms; the docs say which oneanswertakes.)app.serving(): http.Handler(name may follow the repo's participle rule) wrapsanswerintostd/http'sHandler(Result<Response, HttpError>), soServer.bind(...)takes it unchanged; aWebErrornever reaches the transport as anHttpErrorbecauseansweralready rendered it.tests/conformance/web-next-record-capture.trb(recordNextcarryingendpoint: Handlerwith invisible captures, awaited inside a task) and run it in the forge CI. If it checks: keepNextas in WEB.md and record "intended, held by this test" in WEB.md section 8. If the checker refuses it:endpointbecomes a trait value (trait Endpoint { fn serve(request): Task<...> }plus an adapter from aHandlerclosure written at the call site), WEB.md section 3.3 gets a "Built" note, and the probe stays as a negative conformance test with the expected error. Either way the loop records the result in the PR.Acceptance criteria
std/web/src/middleware.trb(Middleware,Next) andstd/web/src/app.trb(WebApp,web,answer, thestd/httpadapter) exported fromlib.trb; every public item has a doc comment with an example.tests/conformance/web-next-record-capture.trbexists and its outcome (checks, or refused with the expected error) is recorded in WEB.md section 8 and in the PR body.std/web/tests/app.test.trb(handlers arefns answering aTaskawaited at the test file's top level, F13): two layers run outermost first and unwind in reverse; a layer that does not callnextstops the chain and the handler is not run; a layer that rebuilds the request is seen below it and not above; aFail WebError.notFound()from the handler answersOkwith status 404 and the default page; a customerrorsrenderer is used for 404 and 400; the cause is not in the rendered page;web { }withouthandleand with twohandles is refused.app.serving()throughstd/http's server on a loopback port (or the repo's existing in-memory transport used bystd/httptests) and gets the 404 page and a 200 page back.docs/standard-library/web.mdgains the middleware,web { }andanswersections;skills/regenerated (torb docs skill docs skills); WEB.md status line and slice table say slice 2 is built up toanswer(WebTest and the example remain).feat(std/web): add Middleware and Next(with the probe),feat(std/web): add the web builder and the std/http adapter.Seed
No. New package members, no natives, no breaking change to a published contract.
Size
One PR, two commits,
std/webonly plus the probe, WEB.md and the docs page; about 1 h of implementation on top of #575. Sonnet/medium per the loop contract forstd/; if the probe forces the trait-value fallback it is still the same PR.Dependencies
main),std/httpHandler/Server(onmain).WebTest), #97 (forms and CSRF), #98 (sessions and cookies), #100 (assets), #103 (std/live).Part of #94.
std/webcore (WEB slice 2) #94std/webcore (WEB slice 2) #94std/webcore (WEB slice 2) #94[loop:torbscript] claim implement by session:loop-torbscript-20261114 until 2026-11-14T06:30:00Z
[loop:torbscript] checkpoint: worktree loop/776-web-middleware-app, commit 1 (Middleware, Next, probe) pushed, PR #777 open as WIP. No local toolchain runs here (torb needs a newer glibc), so CI is the first check. next: read the probe result, then commit 2 (WebApp, web builder, adapter, docs).
[loop:torbscript] checkpoint: PR #777 has both commits pushed (Middleware/Next + probe; WebApp, web builder, adapter, tests, docs, skills mirror, WEB.md). CI is queued (forge runners backed up, many runs waiting), no result yet. Nothing could run locally (torb needs a newer glibc). next: read CI, fix what it finds.
[loop:torbscript] implement: claim released (worker run timed out at the 1 h lease). PR #777 has both commits pushed, CI queued behind the runner backlog, no result yet. next: read CI on PR #777, then review (argus).
std/webcore (WEB slice 2) #94std/livepage placement (WEB slice 6) and the client script (slice 7) #103[loop:torbscript] claim implement by session:loop-torbscript-20261114 until 2026-11-14T10:40Z
[loop:torbscript] review-fix: new head
e7b46cd4. Cause: tier A and gates ran red on7f61a966for six gates: the doc examples of WebApp, web, tryWeb and Next were not self-contained (doc --check --no-run std), Middleware.around, Next.layers, Next.endpoint and Next.run had no doc comment, docs/standard-library/web.md and its skills mirror were stale (docs declarations --check,docs skill --check), two files were not formatted (std/web/tests/app.test.trb, tests/conformance/web-next-record-capture.trb), and the loopback test called a task's result inside a task (panicrunToEnd() was called inside a task, native and VM). Fix (one fix commit on top, pushed to the branch): self-contained examples, doc comments,overLoopbacknow returnsTask<String>and is awaited at the top level, formatted, docs and skills regenerated. Verified locally with the nightly torb one7b46cd4:torb format --check std/web tests/conformance(0 of 394 would change),torb check .(1188 files, no problems),torb docs declarations --check,torb docs skill docs skills --check,torb docs index --check(all up to date),torb doc --check --no-run std/web(16 examples, no problems),torb test std/webon the VM (58 passed, 0 failed), conformance probe web-next-record-capture matches its .expected,tools/check-commits.sh origin/main HEADclean. Not run locally: native tests and the full std package suites (no C compiler here),docs check(needs one). next: wait for CI, then review (argus)