web: std/web core (WEB slice 2) #94

Open
opened 2026-10-05 21:07:19 +00:00 by orion · 4 comments
Collaborator

Context

Handler, WebApp, web { }, middleware, WebError, response helpers, WebTest. This is an epic: it is a new package with an error type, an application builder, a middleware chain, request and response helpers, a test helper and an example application, which is more than one PR. Steps become issues just in time, one at a time.

Source

Design record point

WEB.md sections 3.1 to 3.4, 3.8, 3.9, slice 2 of section 10, decisions D5 and D13. Decided by the loop (full reasoning in the comment below):

  • One package std/web; Request and Response are std/http's shared types, extended by extend, not redefined.
  • Routing is D5 as decided: URI templates and TemplateRoutes of std/uri, not the hand-written match of the section 3.2 example. WebTest.assertRoutes is the round trip over TemplateRoutes.link and matched.
  • Typed request.query<Value>() goes with the form-urlencoded Decoder of #97; slice 2 ships the raw query accessors. Cookies and withCookie are #98, assets is #100, the HTML page reader of WebTest waits on the tree of #79.
  • Touches the seed? No for the whole epic: new package, no natives, no std name looked up by string.

Steps

  • 1. std/web package: WebError, Handler, the request and response helpers, docs page (filed as #575)
  • 2. Middleware, Next, WebApp, the web { } builder (wrap, handle, errors), the adapter to std/http's handler, app.answer(request); a conformance probe for the record-carrying-a-function question of WEB.md section 8
  • 3. WebTest: Request.get, Request.post(form:), assertRoutes; the answered page checked as text until #79's tree exists
  • 4. examples/web-shop: the orders pages on std/html, routes by URI template, a request-log middleware, tests through app.answer, docs updated, WEB.md slice 2 marked built

Dependencies

  • Needs: #93 (closed), std/http Request/Response/Handler (on main), std/uri TemplateRoutes (on main).
  • Unblocks: #97 (forms and CSRF), #98 (sessions and cookies), #100 (assets), #103 (std/live).
  • Independent of #79 except the optional page reader of step 3.

Moved from the planning lists of the docs; the design record stays the specification.

## Context `Handler`, `WebApp`, `web { }`, middleware, `WebError`, response helpers, `WebTest`. This is an epic: it is a new package with an error type, an application builder, a middleware chain, request and response helpers, a test helper and an example application, which is more than one PR. Steps become issues just in time, one at a time. ## Source - [WEB.md slice 2](https://git.torb.dev/torbscript/language/src/commit/4b59fae049eb4348fdb204d8849ed29997cbb797/docs/design/WEB.md#L975) ## Design record point WEB.md sections 3.1 to 3.4, 3.8, 3.9, slice 2 of section 10, decisions D5 and D13. Decided by the loop (full reasoning in the comment below): - One package `std/web`; `Request` and `Response` are `std/http`'s shared types, extended by `extend`, not redefined. - Routing is D5 as decided: URI templates and `TemplateRoutes` of `std/uri`, not the hand-written `match` of the section 3.2 example. `WebTest.assertRoutes` is the round trip over `TemplateRoutes.link` and `matched`. - Typed `request.query<Value>()` goes with the form-urlencoded `Decoder` of #97; slice 2 ships the raw query accessors. Cookies and `withCookie` are #98, `assets` is #100, the HTML page reader of `WebTest` waits on the tree of #79. - Touches the seed? No for the whole epic: new package, no natives, no std name looked up by string. ## Steps - [ ] 1. `std/web` package: `WebError`, `Handler`, the request and response helpers, docs page (filed as #575) - [ ] 2. `Middleware`, `Next`, `WebApp`, the `web { }` builder (`wrap`, `handle`, `errors`), the adapter to `std/http`'s handler, `app.answer(request)`; a conformance probe for the record-carrying-a-function question of WEB.md section 8 - [ ] 3. `WebTest`: `Request.get`, `Request.post(form:)`, `assertRoutes`; the answered page checked as text until #79's tree exists - [ ] 4. `examples/web-shop`: the orders pages on `std/html`, routes by URI template, a request-log middleware, tests through `app.answer`, docs updated, WEB.md slice 2 marked built ## Dependencies - Needs: #93 (closed), `std/http` `Request`/`Response`/`Handler` (on `main`), `std/uri` `TemplateRoutes` (on `main`). - Unblocks: #97 (forms and CSRF), #98 (sessions and cookies), #100 (assets), #103 (`std/live`). - Independent of #79 except the optional page reader of step 3. _Moved from the planning lists of the docs; the design record stays the specification._
Author
Collaborator

[loop:torbscript] intake: in scope (author orion, label loop), source WEB.md slice 2, not a duplicate. Large (Handler, WebApp, web { } block, middleware, examples/web-shop); refine splits it. Depends on #93. next: waiting (dependency #93) until 2026-10-10T04:00:00Z

[loop:torbscript] intake: in scope (author orion, label loop), source WEB.md slice 2, not a duplicate. Large (`Handler`, `WebApp`, `web { }` block, middleware, `examples/web-shop`); refine splits it. Depends on #93. next: waiting (dependency #93) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261029 until 2026-10-29T10:20Z

[loop:torbscript] claim refine by session:loop-torbscript-20261029 until 2026-10-29T10:20Z
Author
Collaborator

[loop:torbscript] decided by the loop: #94 is too big for one PR, so it is an epic and stays without ready. The slice names a package, an error type, an application builder with a block, a middleware chain over a trait, request and response helpers, a test helper and an example application; four reviewable pieces, each needing the previous. Step 1 is filed as #575 (keyword search of open issues for web, Handler, std/web, WebTest, middleware found only the sibling slices #97, #98, #99, #100, #103, #105, #107, #108, #109, none a duplicate) and is ready; steps 2 to 4 are filed just in time. The dependency #93 is closed, so the earlier waiting note is obsolete; problem 3 of WEB.md section 1 (the generated C for a trait method answering a task) is fixed by acf9360f, held by tests/conformance/task-member-value-parameter.trb.

Decisions:

  • Request and Response stay std/http's shared types. WEB.md section 9 assumed values (a middleware copys a request, a participle answers a copy). They own a one-shot body stream, so: a middleware that changes the request builds a new Request from the parts of the old one and takes its body over; withStatus/withHeader answer a new Response over the same Body, and the old binding is not read afterwards. Step 1 documents this, tests it, and adds a "Built" note to WEB.md section 3.4.
  • Names that already exist in std/http: Request.segments() (percent-encoded) and Response.jsonOf stay; std/web adds decodedSegments() and uses jsonOf. Handler of std/web is its own type (Result<Response, WebError>) and is not a re-export of std/http's; programs needing both qualify std/http.
  • Routing follows D5 as decided (URI templates), not the match of WEB.md section 3.2, which that section's example predates. WebTest.assertRoutes(routes, samples) checks matched(link(sample)) == sample for each sample; the startup check already lives in TemplateRoutes.of.
  • request.query<Value: Decode>() moves to #97. It needs a form-urlencoded Decoder, which the forms slice builds anyway for request.form; building it here would be that slice's work in this one. Slice 2 gets queryParameters() and queryParameter(name) over String.formDecoded (std/uri). request.json, request.cookie, withCookie and assets follow with #97, #98, #100.
  • WebTest's page reader (page.text("h1")) waits for #79's tree (steps 2 to 4 of that epic). Until then tests read the markup with html.text() and string checks; step 3 notes this, no new issue.
  • A test body cannot await yet (F13). Handler tests are fns answering a Task that the test file awaits at its top level, as WEB.md section 3.9 says.
  • assets is not in WebApp's vocabulary in slice 2 (#100 adds it); the receiver starts with wrap, handle, errors.
  • If Next carrying endpoint: Handler is refused by the task capture rule (WEB.md section 8, "one question for the checker"), step 2 makes endpoint a trait value, as the record says, and records the result; the conformance probe is part of step 2.
  • Touches the seed? No: new package, no natives, no std name looked up by string.
[loop:torbscript] decided by the loop: #94 is too big for one PR, so it is an epic and stays without `ready`. The slice names a package, an error type, an application builder with a block, a middleware chain over a trait, request and response helpers, a test helper and an example application; four reviewable pieces, each needing the previous. Step 1 is filed as #575 (keyword search of open issues for web, Handler, std/web, WebTest, middleware found only the sibling slices #97, #98, #99, #100, #103, #105, #107, #108, #109, none a duplicate) and is `ready`; steps 2 to 4 are filed just in time. The dependency #93 is closed, so the earlier waiting note is obsolete; problem 3 of WEB.md section 1 (the generated C for a trait method answering a task) is fixed by `acf9360f`, held by `tests/conformance/task-member-value-parameter.trb`. Decisions: - **`Request` and `Response` stay `std/http`'s `shared type`s.** WEB.md section 9 assumed values (a middleware `copy`s a request, a participle answers a copy). They own a one-shot body stream, so: a middleware that changes the request builds a new `Request` from the parts of the old one and takes its body over; `withStatus`/`withHeader` answer a new `Response` over the same `Body`, and the old binding is not read afterwards. Step 1 documents this, tests it, and adds a "Built" note to WEB.md section 3.4. - **Names that already exist in `std/http`:** `Request.segments()` (percent-encoded) and `Response.jsonOf` stay; `std/web` adds `decodedSegments()` and uses `jsonOf`. `Handler` of `std/web` is its own type (`Result<Response, WebError>`) and is not a re-export of `std/http`'s; programs needing both qualify `std/http`. - **Routing follows D5 as decided (URI templates), not the `match` of WEB.md section 3.2**, which that section's example predates. `WebTest.assertRoutes(routes, samples)` checks `matched(link(sample)) == sample` for each sample; the startup check already lives in `TemplateRoutes.of`. - **`request.query<Value: Decode>()` moves to #97.** It needs a form-urlencoded `Decoder`, which the forms slice builds anyway for `request.form`; building it here would be that slice's work in this one. Slice 2 gets `queryParameters()` and `queryParameter(name)` over `String.formDecoded` (`std/uri`). `request.json`, `request.cookie`, `withCookie` and `assets` follow with #97, #98, #100. - **`WebTest`'s page reader (`page.text("h1")`) waits for #79's tree** (steps 2 to 4 of that epic). Until then tests read the markup with `html.text()` and string checks; step 3 notes this, no new issue. - **A `test` body cannot await yet (F13).** Handler tests are `fn`s answering a `Task` that the test file awaits at its top level, as WEB.md section 3.9 says. - **`assets` is not in `WebApp`'s vocabulary in slice 2** (#100 adds it); the receiver starts with `wrap`, `handle`, `errors`. - **If `Next` carrying `endpoint: Handler` is refused by the task capture rule** (WEB.md section 8, "one question for the checker"), step 2 makes `endpoint` a trait value, as the record says, and records the result; the conformance probe is part of step 2. - Touches the seed? No: new package, no natives, no std name looked up by string.
Author
Collaborator

[loop:torbscript] refine: released the claim of session:loop-torbscript-20261029. #94 is an epic (checklist of 4 steps in the body), step 1 filed as #575 with label ready; the decisions are in the comment above. Nothing was written to the repo. Worker usage: about 120k tokens (sonnet).
next: waiting (epic waits on its steps)

[loop:torbscript] refine: released the claim of session:loop-torbscript-20261029. #94 is an epic (checklist of 4 steps in the body), step 1 filed as #575 with label `ready`; the decisions are in the comment above. Nothing was written to the repo. Worker usage: about 120k tokens (sonnet). next: waiting (epic waits on its steps)
Sign in to join this conversation.
No labels
loop
ready
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language#94
No description provided.