@@ -130,27 +130,44 @@ The response is *mutable* — fill it in, don't return it.
130130| ` res.sendText(code, body) ` | reply ` code ` with a plain-text body |
131131
132132` res.send ` / ` req.parse ` work for any ` event ` or compound ` type ` ; the compiler
133- derives the codec automatically. A field may be a ` List<T> ` — it is stored
134- growably in memory and serialized as a JSON array, so a service can accumulate
135- posted items and hand the list straight back. A request that no controller
136- matches gets a ` 404 ` .
133+ derives the codec automatically. The payload can also be a ** bare ` List<T> ` or
134+ ` T[] ` ** — it serializes to a JSON array, so a controller can return a
135+ repository's list directly with no wrapper DTO:
136+
137+ ``` x
138+ action handle(req: HttpRequest, res: HttpResponse) {
139+ res.send(users.findAll()) // -> [ {...}, {...} ] (List<User>)
140+ }
141+ ```
142+
143+ A ` List<T> ` field on a DTO works the same way, and is stored growably in memory,
144+ so a service can accumulate posted items and hand the list straight back. A
145+ request that no controller matches gets a ` 404 ` .
137146
138147### Keeping state across requests
139148
140- A service that accumulates data across requests must be bound ** ` as singleton ` **
141- so every request shares one instance:
149+ A service that accumulates data across requests must be a ** singleton** so every
150+ request shares one instance — either mark the dependency at the injection site
151+ or bind it in the module:
142152
143153``` x
144154class ItemStore implements Store {
145155 state { items: List<Item> = empty List<Item> }
146156 consumer add(it: Item) { this.items.push(it) }
147157 ...
148158}
149- module App { bind Store -> ItemStore as singleton } // shared, not per-resolve
159+
160+ class ItemController implements WebRequestHandler {
161+ deps { store: Store as singleton } // marker: one shared store
162+ ...
163+ }
164+ // — or, equivalently, in the module —
165+ module App { bind Store -> ItemStore as singleton }
150166```
151167
152- Without ` as singleton ` the service is resolved fresh each time and its state
153- never accumulates. See ` examples/web/web_store_demo.xi ` for the full pattern.
168+ Left transient (the default), the service is resolved fresh each request and its
169+ state never accumulates. See ` examples/web/web_store_demo.xi ` for the full
170+ pattern.
154171
155172## The ` WebTransport `
156173
@@ -186,6 +203,19 @@ $ xc app.xi && ./build/app
186203web: serving on http://0.0.0.0:8080
187204```
188205
206+ ` web.serve ` blocks until the server is stopped. ** ` web.shutdown() ` ** stops it —
207+ it unblocks ` serve ` so the call returns and the process exits. It is safe to call
208+ from another thread, so a background timer can bound the server's lifetime (the
209+ serve demos use this so a run never blocks):
210+
211+ ``` x
212+ async entry main(args: String[]) -> Integer {
213+ runWithDelay(30000) { web.shutdown() } // self-terminate after 30s
214+ web.serve(8080)
215+ return 0
216+ }
217+ ```
218+
189219## HTTPS
190220
191221` web.serveTLS(port, certPath, keyPath) ` runs the same handler stack over TLS.
0 commit comments