camber.bend

bend-kit-camber@0.6.0.0 · 1,198 lines · 0 definitions

1# Fixed prepared HTTP applications and bounded affine dependency bundles.
2import Base
3import bend-kit-http@0.30.0.0/http.bend as Http
4import bend-kit-router@0.2.0.0/router.bend as Router
5import bend-kit-router@0.2.0.0/target.bend as Target
6import bend-kit-json@0.5.1.0/json.bend as Json
7import bend-kit-camber@0.6.0.0/response.bend as Response
8import bend-kit-camber@0.6.0.0/input.bend as Input
9import bend-kit-http@0.30.0.0/completion.bend as TransportCompletion
10import bend-kit-camber@0.6.0.0/notices.bend as Notices
11import bend-kit-time@0.1.0.0/time.bend as Time
12
13type ResponseError is Data:
14  InvalidResponse{}
15  InvalidRedirect{}
16
17def validate.checked(valid: Bool, response: Http.Res) -> Result<&2, &1, ResponseError, Http.Res>:
18  match valid:
19    case True{}:
20      Done{response}
21    case False{}:
22      Fail{InvalidResponse{}}
23
24def validate(response: Http.Res) -> Result<&2, &1, ResponseError, Http.Res>:
25  Http.Res{+status, +headers, body} = response
26  validate.checked(Response.valid(status, headers), Http.Res{status, headers, body})
27
28# The encoder is an explicit public projection, never record reflection.
29def json(~T: Type, ~encode: T -> Json.Val, status: U32, value: T) -> Http.Res:
30  Http.Res{status, Http.set(Http.empty(), "content-type", "application/json"), Json.encode.bytes(encode(value))}
31
32def text(status: U32, value: String) -> Http.Res:
33  Http.Res{status, Http.set(Http.empty(), "content-type", "text/plain; charset=utf-8"), Json.utf8(value)}
34
35def raw(status: U32, body: Http.Body) -> Http.Res:
36  Http.Res{status, Http.empty(), body}
37
38def empty(status: U32) -> Http.Res:
39  raw(status, Http.from_string(""))
40
41def redirect.checked(valid: Bool, status: U32, location: String) -> Result<&2, &1, ResponseError, Http.Res>:
42  match valid:
43    case False{}:
44      Fail{InvalidRedirect{}}
45    case True{}:
46      Done{Http.Res{status, Http.set(Http.empty(), "location", location), Http.from_string("")}}
47
48def redirect(+status: U32, +location: String) -> Result<&2, &1, ResponseError, Http.Res>:
49  redirect.checked((U32.is_eq(status, 301) || U32.is_eq(status, 302) || U32.is_eq(status, 303) || U32.is_eq(status, 307) || U32.is_eq(status, 308)) && Bool.not(String.eq(location, "")) && Response.value(location), status, location)
50
51type Counts is Data:
52  Counts{capacity: Nat, in_use: Nat, completed: Nat, rejected: Nat}
53
54type Outcome<-E: Data, -O: Data> is Data:
55  Success{value: O}
56  Expected{error: E}
57  Exhausted{}
58  Stopped{}
59
60type CloseResult is Data:
61  Closed{}
62  Busy{counts: Counts}
63
64type Message<-R: Type, -P: Data, -I: Data, -E: Data, -O: Data> is Type:
65  Submit{principal: P, input: I, reply: Chan(Outcome<E, O>)}
66  Returned{resource: R}
67  Inspect{reply: Chan(Counts)}
68  Shutdown{reply: Chan(CloseResult)}
69
70# Copy only this context, never the resources. The inbox bounds queued commands;
71# callers must count blocked submissions inside their transport admission limit.
72type Context<-R: Type, -P: Data, -I: Data, -E: Data, -O: Data> is Data:
73  Context{messages: Chan(Message<R, P, I, E, O>)}
74
75# The completion future has one affine owner; request contexts copy only inbox.
76type Owner<-R: Type, -P: Data, -I: Data, -E: Data, -O: Data> is Type:
77  Owner{context: Context<R, P, I, E, O>, finished: Chan(Unit)}
78
79type OwnerClose<-R: Type, -P: Data, -I: Data, -E: Data, -O: Data> is Type:
80  OwnerClosed{}
81  OwnerBusy{owner: Owner<R, P, I, E, O>, counts: Counts}
82
83def owner.context(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data,
84  owner: Owner<R, P, I, E, O>
85) -> Owner<R, P, I, E, O> & Context<R, P, I, E, O>:
86  Owner{+context, finished} = owner
87  (Owner{context, finished}, context)
88
89def outcome.value(-E: Data, -O: Data, result: Result<&2, &2, E, O>) -> Outcome<E, O>:
90  match result:
91    case Fail{error}:
92      Expected{error}
93    case Done{value}:
94      Success{value}
95
96# The resources return through the private owner protocol BEFORE a disposable
97# caller reply. A closed caller reply cannot erase a handle or strand capacity.
98def work.return(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data,
99  messages: Chan(Message<R, P, I, E, O>), reply: Chan(Outcome<E, O>),
100  result: R & Result<&2, &2, E, O>
101) -> IO(Unit):
102  (returned, outcome) = result
103  do IO<Unit>:
104    accepted : Bool <- Chan.send(Message<R, P, I, E, O>, messages, Returned{returned})
105    sent : Bool <- Chan.send(Outcome<E, O>, reply, outcome.value(E, O, outcome))
106    return Unit{}
107
108def work(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
109  ~handler: R -> P -> I -> IO(R & Result<&2, &2, E, O>),
110  resource: R, principal: P, input: I,
111  messages: Chan(Message<R, P, I, E, O>), reply: Chan(Outcome<E, O>)
112) -> IO(Unit):
113  do IO<Unit>:
114    result : R & Result<&2, &2, E, O> <- handler(resource, principal, input)
115    work.return(R, P, I, E, O, messages, reply, result)
116
117def dispose(~R: Type, ~destroy: R -> IO(Unit), resources: List<R>) -> IO(Unit):
118  match resources:
119    case Nil{}:
120      IO.pure(Unit, Unit{})
121    case Con{resource, rest}:
122      do IO<Unit>:
123        destroy(resource)
124        dispose(~R, ~destroy, rest)
125
126# Closing Base channels retains buffered messages. Drain them and answer stopped
127# callers, rather than stranding accepted commands behind the close command.
128def drain.message(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
129  ~destroy: R -> IO(Unit), message: Message<R, P, I, E, O>
130) -> IO(Unit):
131  match message:
132    case Submit{principal, input, reply}:
133      do IO<Unit>:
134        sent : Bool <- Chan.send(Outcome<E, O>, reply, Stopped{})
135        return Unit{}
136    case Inspect{reply}:
137      Chan.close(Counts, reply)
138    case Shutdown{reply}:
139      do IO<Unit>:
140        sent : Bool <- Chan.send(CloseResult, reply, Closed{})
141        return Unit{}
142    case Returned{resource}:
143      destroy(resource)
144
145def drain.step(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
146  ~destroy: R -> IO(Unit), pending: Maybe<&1, Message<R, P, I, E, O>>,
147  next: Unit -> IO(Unit)
148) -> IO(Unit):
149  match pending:
150    case None{}:
151      IO.pure(Unit, Unit{})
152    case Some{message}:
153      do IO<Unit>:
154        drain.message(~R, ~P, ~I, ~E, ~O, ~destroy, message)
155        next(Unit{})
156
157# The closed inbox contains at most room buffered messages.
158def drain(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
159  ~destroy: R -> IO(Unit), fuel: Nat, +messages: Chan(Message<R, P, I, E, O>)
160) -> IO(Unit):
161  match fuel:
162    case 0n:
163      IO.pure(Unit, Unit{})
164    case 1n+p:
165      do IO<Unit>:
166        pending : Maybe<&1, Message<R, P, I, E, O>> <- Chan.recv(Message<R, P, I, E, O>, messages)
167        drain.step(~R, ~P, ~I, ~E, ~O, ~destroy, pending, u => drain(~R, ~P, ~I, ~E, ~O, ~destroy, p, messages))
168
169type OwnerStep<-R: Type, -P: Data, -I: Data, -E: Data, -O: Data> is Type:
170  Waiting{resources: List<R>, counts: Counts}
171  Received{message: Message<R, P, I, E, O>, counts: Counts, resources: List<R>}
172
173# IO.join closes a one-shot channel; the owner inbox must remain reusable.
174def receive.value(-A: Type, value: Maybe<&1, A>) -> IO(A):
175  match value:
176    case None{}:
177      IO.die(A, 1, "camber internal channel closed unexpectedly")
178    case Some{message}:
179      IO.pure(A, message)
180
181def receive(-A: Type, channel: Chan(A)) -> IO(A):
182  do IO<A>:
183    value : Maybe<&1, A> <- Chan.recv(A, channel)
184    receive.value(A, value)
185
186# Only idle close ends this loop. Busy close never waits on a stalled handler.
187# @unsafe is solely the lifecycle receive loop, not a termination/proof claim.
188@unsafe def owner(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
189  ~handler: R -> P -> I -> IO(R & Result<&2, &2, E, O>), ~destroy: R -> IO(Unit),
190  +messages: Chan(Message<R, P, I, E, O>), +room: U32, step: OwnerStep<R, P, I, E, O>
191) -> IO(Unit):
192  match step:
193    case Waiting{resources, counts}:
194      do IO<Unit>:
195        message : Message<R, P, I, E, O> <- receive(Message<R, P, I, E, O>, messages)
196        owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Received{message, counts, resources})
197    case Received{message, counts, resources}:
198      match message:
199        case Returned{resource}:
200          Counts{capacity, in_use, completed, rejected} = counts
201          owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Waiting{resource <> resources, Counts{capacity, Nat.sub(in_use, 1n), 1n+completed, rejected}})
202        case Inspect{reply}:
203          Counts{+capacity, +in_use, +completed, +rejected} = counts
204          do IO<Unit>:
205            sent : Bool <- Chan.send(Counts, reply, Counts{capacity, in_use, completed, rejected})
206            owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Waiting{resources, Counts{capacity, in_use, completed, rejected}})
207        case Shutdown{reply}:
208          Counts{+capacity, in_use, +completed, +rejected} = counts
209          match in_use:
210            case 0n:
211              do IO<Unit>:
212                dispose(~R, ~destroy, resources)
213                Chan.close(Message<R, P, I, E, O>, messages)
214                sent : Bool <- Chan.send(CloseResult, reply, Closed{})
215                drain(~R, ~P, ~I, ~E, ~O, ~destroy, U32.to_nat(room), messages)
216            case 1n+ +p:
217              do IO<Unit>:
218                sent : Bool <- Chan.send(CloseResult, reply, Busy{Counts{capacity, 1n+p, completed, rejected}})
219                owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Waiting{resources, Counts{capacity, 1n+p, completed, rejected}})
220        case Submit{principal, input, reply}:
221          Counts{capacity, in_use, completed, rejected} = counts
222          match resources:
223            case Nil{}:
224              do IO<Unit>:
225                sent : Bool <- Chan.send(Outcome<E, O>, reply, Exhausted{})
226                owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Waiting{Nil{}, Counts{capacity, in_use, completed, 1n+rejected}})
227            case Con{resource, rest}:
228              do IO<Unit>:
229                IO.spawn(Unit, work(~R, ~P, ~I, ~E, ~O, ~handler, resource, principal, input, messages, reply))
230                owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Waiting{rest, Counts{capacity, 1n+in_use, completed, rejected}})
231
232def build.step(~R: Type, ~E: Data, ~destroy: R -> IO(Unit),
233  result: Result<&2, &1, E, R>, resources: List<R>,
234  next: List<R> -> IO(Result<&2, &1, E, List<R>>)
235) -> IO(Result<&2, &1, E, List<R>>):
236  match result:
237    case Fail{error}:
238      do IO<Result<&2, &1, E, List<R>>>:
239        dispose(~R, ~destroy, resources)
240        return Fail{error}
241    case Done{resource}:
242      next(resource <> resources)
243
244def build(~K: Data, ~R: Type, ~E: Data, ~create: K -> Nat -> IO(Result<&2, &1, E, R>), ~destroy: R -> IO(Unit),
245  remaining: Nat, +config: K, resources: List<R>
246) -> IO(Result<&2, &1, E, List<R>>):
247  match remaining:
248    case 0n:
249      IO.pure(Result<&2, &1, E, List<R>>, Done{resources})
250    case 1n+ +p:
251      do IO<Result<&2, &1, E, List<R>>>:
252        result : Result<&2, &1, E, R> <- create(config, p)
253        build.step(~R, ~E, ~destroy, result, resources, next => build(~K, ~R, ~E, ~create, ~destroy, p, config, next))
254
255def start.ready(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
256  ~handler: R -> P -> I -> IO(R & Result<&2, &2, E, O>), ~destroy: R -> IO(Unit),
257  capacity: Nat, +room: U32, result: Result<&2, &1, E, List<R>>
258) -> IO(Result<&2, &1, E, Owner<R, P, I, E, O>>):
259  match result:
260    case Fail{error}:
261      IO.pure(Result<&2, &1, E, Owner<R, P, I, E, O>>, Fail{error})
262    case Done{resources}:
263      do IO<Result<&2, &1, E, Owner<R, P, I, E, O>>>:
264        channel : Chan(Message<R, P, I, E, O>) <- Chan.new(Message<R, P, I, E, O>, room)
265        +messages : Chan(Message<R, P, I, E, O>) = channel
266        finished : Chan(Unit) <- IO.fork(Unit, owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Waiting{resources, Counts{capacity, 0n, 0n, 0n}}))
267        return Done{Owner{Context{messages}, finished}}
268
269# The initializer closes partial handles within a failed bundle; start closes
270# all prior whole bundles. No workers exist until the complete set opens.
271def start(~K: Data, ~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
272  ~create: K -> Nat -> IO(Result<&2, &1, E, R>),
273  ~handler: R -> P -> I -> IO(R & Result<&2, &2, E, O>), ~destroy: R -> IO(Unit),
274  config: K, +capacity: Nat, room: U32
275) -> IO(Result<&2, &1, E, Owner<R, P, I, E, O>>):
276  do IO<Result<&2, &1, E, Owner<R, P, I, E, O>>>:
277    result : Result<&2, &1, E, List<R>> <- build(~K, ~R, ~E, ~create, ~destroy, capacity, config, Nil{})
278    start.ready(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, capacity, room, result)
279
280def submit(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data,
281  context: Context<R, P, I, E, O>, principal: P, input: I, reply: Chan(Outcome<E, O>)
282) -> IO(Bool):
283  Context{messages} = context
284  Chan.send(Message<R, P, I, E, O>, messages, Submit{principal, input, reply})
285
286def call.accepted(-E: Data, -O: Data, accepted: Bool, +reply: Chan(Outcome<E, O>)) -> IO(Outcome<E, O>):
287  match accepted:
288    case False{}:
289      do IO<Outcome<E, O>>:
290        Chan.close(Outcome<E, O>, reply)
291        return Stopped{}
292    case True{}:
293      do IO<Outcome<E, O>>:
294        result : Outcome<E, O> <- IO.join(Outcome<E, O>, reply)
295        Chan.close(Outcome<E, O>, reply)
296        return result
297
298def call(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data,
299  context: Context<R, P, I, E, O>, principal: P, input: I
300) -> IO(Outcome<E, O>):
301  do IO<Outcome<E, O>>:
302    channel : Chan(Outcome<E, O>) <- Chan.new(Outcome<E, O>, 1)
303    +reply : Chan(Outcome<E, O>) = channel
304    accepted : Bool <- submit(R, P, I, E, O, context, principal, input, reply)
305    call.accepted(E, O, accepted, reply)
306
307def inspect.value(value: Maybe<&1, Counts>) -> Maybe<&2, Counts>:
308  match value:
309    case None{}:
310      None{}
311    case Some{counts}:
312      Some{counts}
313
314def inspect.sent(sent: Bool, +reply: Chan(Counts)) -> IO(Maybe<&2, Counts>):
315  match sent:
316    case False{}:
317      do IO<Maybe<&2, Counts>>:
318        Chan.close(Counts, reply)
319        return None{}
320    case True{}:
321      do IO<Maybe<&2, Counts>>:
322        value : Maybe<&1, Counts> <- Chan.recv(Counts, reply)
323        Chan.close(Counts, reply)
324        return inspect.value(value)
325
326def inspect(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data, context: Context<R, P, I, E, O>) -> IO(Maybe<&2, Counts>):
327  Context{messages} = context
328  do IO<Maybe<&2, Counts>>:
329    channel : Chan(Counts) <- Chan.new(Counts, 1)
330    +reply : Chan(Counts) = channel
331    sent : Bool <- Chan.send(Message<R, P, I, E, O>, messages, Inspect{reply})
332    inspect.sent(sent, reply)
333
334def close.sent(sent: Bool, +reply: Chan(CloseResult)) -> IO(CloseResult):
335  match sent:
336    case False{}:
337      do IO<CloseResult>:
338        Chan.close(CloseResult, reply)
339        return Closed{}
340    case True{}:
341      do IO<CloseResult>:
342        value : CloseResult <- IO.join(CloseResult, reply)
343        Chan.close(CloseResult, reply)
344        return value
345
346def close.join(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data,
347  owner: Owner<R, P, I, E, O>
348) -> IO(OwnerClose<R, P, I, E, O>):
349  Owner{context, finished} = owner
350  do IO<OwnerClose<R, P, I, E, O>>:
351    IO.join(Unit, finished)
352    return OwnerClosed{}
353
354def close.completed(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data,
355  result: CloseResult, owner: Owner<R, P, I, E, O>
356) -> IO(OwnerClose<R, P, I, E, O>):
357  match result:
358    case Closed{}:
359      close.join(R, P, I, E, O, owner)
360    case Busy{counts}:
361      IO.pure(OwnerClose<R, P, I, E, O>, OwnerBusy{owner, counts})
362
363# Stop external admissions before close. Busy preserves live context/capacity;
364# retry after admitted operations finish. Stalled operations remain observable.
365def close(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data, owner: Owner<R, P, I, E, O>) -> IO(OwnerClose<R, P, I, E, O>):
366  Owner{Context{+messages}, finished} = owner
367  do IO<OwnerClose<R, P, I, E, O>>:
368    channel : Chan(CloseResult) <- Chan.new(CloseResult, 1)
369    +reply : Chan(CloseResult) = channel
370    sent : Bool <- Chan.send(Message<R, P, I, E, O>, messages, Shutdown{reply})
371    result : CloseResult <- close.sent(sent, reply)
372    close.completed(R, P, I, E, O, result, Owner{Context{messages}, finished})
373
374# Policies are prepared Data, not callbacks. Later lifecycle stages can consume
375# these root-to-route declarations without splitting patterns or finding groups.
376type Group<-P: Data> is Data:
377  Group{id: U32, ancestors: List<&2, U32>, policy: P}
378
379type GroupPlan<-P: Data> is Data:
380  GroupPlan{ids: List<&2, U32>, policies: List<&2, P>}
381
382type Route<-A: Data, -P: Data> is Data:
383  Route{method: String, pattern: String, action: A, groups: List<&2, U32>, policy: P}
384
385type Endpoint<-A: Data, -P: Data> is Data:
386  Endpoint{action: A, policies: List<&2, P>}
387
388type Application<-A: Data, -C: Data, -P: Data> is Data:
389  Application{table: Router.Table<Endpoint<A, P>>, context: C, root: P, groups: List<&2, GroupPlan<P>>}
390
391type RegistrationError is Data:
392  RouteError{error: Router.Error}
393  InvalidGroups{groups: List<&2, U32>}
394
395type Match<-P: Data> is Data:
396  Match{target: Target.Target, params: Map<&2, String>, groups: List<&2, U32>, method: String, pattern: String, policies: List<&2, P>}
397
398def groups.equal(xs: List<&2, U32>, ys: List<&2, U32>) -> Bool:
399  match xs ys:
400    case Nil{} Nil{}:
401      True{}
402    case x <> xt y <> yt:
403      U32.is_eq(x, y) && groups.equal(xt, yt)
404    case _ _:
405      False{}
406
407def group.pick(~P: Data, same: Bool, group: Group<P>, later: Unit -> Maybe<&2, Group<P>>) -> Maybe<&2, Group<P>>:
408  match same:
409    case True{}:
410      Some{group}
411    case False{}:
412      later(Unit{})
413
414def group.find(~P: Data, groups: List<&2, Group<P>>, +id: U32) -> Maybe<&2, Group<P>>:
415  match groups:
416    case Nil{}:
417      None{}
418    case Group{+here, ancestors, policy} <> rest:
419      group.pick(~P, U32.is_eq(here, id), Group{here, ancestors, policy}, u => group.find(~P, rest, id))
420
421def group.checked(~P: Data, same: Bool, policy: P) -> Maybe<&2, P>:
422  match same:
423    case False{}:
424      None{}
425    case True{}:
426      Some{policy}
427
428def group.policy(~P: Data, found: Maybe<&2, Group<P>>, ancestors: List<&2, U32>) -> Maybe<&2, P>:
429  match found:
430    case None{}:
431      None{}
432    case Some{Group{id, expected, policy}}:
433      group.checked(~P, groups.equal(expected, ancestors), policy)
434
435def groups.policies(~P: Data, ids: List<&2, U32>, +declared: List<&2, Group<P>>, +ancestors: List<&2, U32>) -> Maybe<&2, List<&2, P>>:
436  match ids:
437    case Nil{}:
438      Some{Nil{}}
439    case +id <> rest:
440      do Maybe<&2, List<&2, P>>:
441        policy : P <- group.policy(~P, group.find(~P, declared, id), ancestors)
442        policies : List<&2, P> <- groups.policies(~P, rest, declared, List.append(&2, U32, ancestors, [id]))
443        return policy <> policies
444
445def groups.unique(~P: Data, found: Maybe<&2, Group<P>>) -> Bool:
446  match found:
447    case None{}:
448      True{}
449    case Some{group}:
450      False{}
451
452def groups.valid(~P: Data, groups: List<&2, Group<P>>, +all: List<&2, Group<P>>) -> Bool:
453  match groups:
454    case Nil{}:
455      True{}
456    case Group{+id, +ancestors, policy} <> +rest:
457      chain = groups.policies(~P, List.append(&2, U32, ancestors, [id]), all, Nil{})
458      groups.unique(~P, group.find(~P, rest, id)) && Maybe.is_some(&2, List<&2, P>, chain) && groups.valid(~P, rest, all)
459
460def routes.policies(~P: Data, result: Maybe<&2, List<&2, P>>, ids: List<&2, U32>) -> Result<&2, &2, RegistrationError, List<&2, P>>:
461  match result:
462    case None{}:
463      Fail{InvalidGroups{ids}}
464    case Some{policies}:
465      Done{policies}
466
467def routes.prepare(~A: Data, ~P: Data, routes: List<&2, Route<A, P>>, +groups: List<&2, Group<P>>, +root: P) -> Result<&2, &2, RegistrationError, List<&2, Router.Entry<Endpoint<A, P>>>>:
468  match routes:
469    case Nil{}:
470      Done{Nil{}}
471    case Route{method, pattern, action, +ids, policy} <> rest:
472      do Result<&2, &2, RegistrationError, List<&2, Router.Entry<Endpoint<A, P>>>>:
473        policies : List<&2, P> <- routes.policies(~P, groups.policies(~P, ids, groups, Nil{}), ids)
474        entries : List<&2, Router.Entry<Endpoint<A, P>>> <- routes.prepare(~A, ~P, rest, groups, root)
475        return Router.Entry{method, pattern, Endpoint{action, root <> List.append(&2, P, policies, [policy])}, ids} <> entries
476
477def groups.prepare(~P: Data, groups: List<&2, Group<P>>, +all: List<&2, Group<P>>) -> Result<&2, &2, RegistrationError, List<&2, GroupPlan<P>>>:
478  match groups:
479    case Nil{}:
480      Done{Nil{}}
481    case Group{id, ancestors, policy} <> rest:
482      +ids = List.append(&2, U32, ancestors, [id])
483      do Result<&2, &2, RegistrationError, List<&2, GroupPlan<P>>>:
484        policies : List<&2, P> <- routes.policies(~P, groups.policies(~P, ids, all, Nil{}), ids)
485        plans : List<&2, GroupPlan<P>> <- groups.prepare(~P, rest, all)
486        return GroupPlan{ids, policies} <> plans
487
488def groups.select(~P: Data, same: Bool, policies: List<&2, P>, later: Unit -> List<&2, P>) -> List<&2, P>:
489  match same:
490    case True{}: policies
491    case False{}: later(Unit{})
492
493def groups.prepared(~P: Data, plans: List<&2, GroupPlan<P>>, +ids: List<&2, U32>) -> List<&2, P>:
494  match plans:
495    case Nil{}:
496      Nil{}
497    case GroupPlan{here, policies} <> rest:
498      groups.select(~P, groups.equal(here, ids), policies, u => groups.prepared(~P, rest, ids))
499
500def application.table(~A: Data, ~C: Data, ~P: Data, result: Result<&2, &2, Router.Error, Router.Table<Endpoint<A, P>>>, context: C, root: P, groups: List<&2, GroupPlan<P>>) -> Result<&2, &2, RegistrationError, Application<A, C, P>>:
501  match result:
502    case Fail{error}:
503      Fail{RouteError{error}}
504    case Done{table}:
505      Done{Application{table, context, root, groups}}
506
507def application.valid(~A: Data, ~C: Data, ~P: Data, valid: Bool, routes: List<&2, Route<A, P>>, +groups: List<&2, Group<P>>, context: C, +root: P) -> Result<&2, &2, RegistrationError, Application<A, C, P>>:
508  match valid:
509    case False{}:
510      Fail{InvalidGroups{Nil{}}}
511    case True{}:
512      do Result<&2, &2, RegistrationError, Application<A, C, P>>:
513        entries : List<&2, Router.Entry<Endpoint<A, P>>> <- routes.prepare(~A, ~P, routes, groups, root)
514        plans : List<&2, GroupPlan<P>> <- groups.prepare(~P, groups, groups)
515        application.table(~A, ~C, ~P, Router.prepare(~Endpoint<A, P>, entries), context, root, plans)
516
517# Construction is pure and fallible; no listener or owner is started here.
518def application(~A: Data, ~C: Data, ~P: Data, routes: List<&2, Route<A, P>>, +groups: List<&2, Group<P>>, context: C, root: P) -> Result<&2, &2, RegistrationError, Application<A, C, P>>:
519  application.valid(~A, ~C, ~P, groups.valid(~P, groups, groups), routes, groups, context, root)
520
521def describe(~A: Data, ~C: Data, ~P: Data, app: Application<A, C, P>) -> List<&2, Router.Description>:
522  Application{table, context, root, groups} = app
523  Router.describe(~Endpoint<A, P>, table)
524
525# A closed template can run repeatedly; an affine callback registry cannot.
526def plain(~handler: Http.Req -> IO(Http.Res), req: Http.Req) -> IO(Result<&2, &1, ResponseError, Http.Res>):
527  do IO<Result<&2, &1, ResponseError, Http.Res>>:
528    response : Http.Res <- handler(req)
529    return validate(response)
530
531type Policy<-H: Data> is Data:
532  Policy{id: U32, before: List<&2, H>, transforms: List<&2, H>}
533
534def policy(~H: Data, id: U32, before: List<&2, H>, transforms: List<&2, H>) -> Policy<H>:
535  Policy{id, before, List.reverse(&2, H, transforms)}
536
537type Metadata is Data:
538  Metadata{method: String, target: String, headers: Map<&2, List<&2, String>>}
539
540type Error<-E: Data> is Data:
541  ApplicationError{error: E}
542  Framework{status: U32, allow: String}
543  ResponseInvalid{}
544
545# Adapt reusable input status failures to the existing one-pass dispatch mapper.
546def input(~I: Type, ~E: Data, outcome: Result<&2, &1, U32, I>) -> Result<&2, &1, Error<E>, I>:
547  match outcome:
548    case Fail{status}: Fail{Framework{status, ""}}
549    case Done{value}: Done{value}
550
551def execute.decoded(~I: Type, ~S: Type, ~K: Data, ~E: Data,
552  ~handler: S -> K -> I -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
553  outcome: Result<&2, &1, Error<E>, I>, state: S, config: K
554) -> IO(S & Result<&2, &1, Error<E>, Http.Res>):
555  match outcome:
556    case Fail{error}: IO.pure(S & Result<&2, &1, Error<E>, Http.Res>, (state, Fail{error}))
557    case Done{value}: handler(state, config, value)
558
559# An executor, not a second dispatcher: call from dispatch's run template, after
560# all applicable before hooks. Only validated typed input reaches business IO.
561def execute(~I: Type, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
562  ~decoder: K -> Match<Policy<H>> -> Http.Req -> Result<&2, &1, Error<E>, I>,
563  ~handler: S -> K -> I -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
564  state: S, +config: K, selected: Match<Policy<H>>, req: Http.Req
565) -> IO(S & Result<&2, &1, Error<E>, Http.Res>):
566  execute.decoded(~I, ~S, ~K, ~E, ~handler, decoder(config, selected, req), state, config)
567
568type Step<-S: Type, -E: Data> is Type:
569  Continue{state: S}
570  Early{state: S, response: Http.Res}
571  Reject{state: S, error: Error<E>}
572
573type Completion<-S: Type, -H: Data> is Type:
574  Completed{state: S, response: Http.Res, entered: List<&2, Policy<H>>, mapped: Bool, stopped: Bool}
575
576# Application completion only: no socket write, duration, or peer-receipt claim.
577def minimal() -> Http.Res:
578  empty(500)
579
580def problem.allow(add: Bool, headers: Map<&2, List<&2, String>>, allow: String) -> Map<&2, List<&2, String>>:
581  match add:
582    case True{}: Http.set(headers, "allow", allow)
583    case False{}: headers
584
585def problem.response(allow: String, response: Http.Res) -> Http.Res:
586  Http.Res{+status, headers, body} = response
587  Http.Res{status, problem.allow(U32.is_eq(status, 405), Http.set(headers, "content-type", "application/problem+json"), allow), body}
588
589def problem(+status: U32, title: String, allow: String) -> Http.Res:
590  problem.response(allow, json(~Json.Val, ~(x => x), status, Json.Obj{[(Json.utf8("type"), Json.Str{Json.utf8("about:blank")}), (Json.utf8("title"), Json.Str{Json.utf8(title)}), (Json.utf8("status"), Json.Num{Json.utf8(U32.show(status))})]}))
591
592def default.error(~E: Data, error: Error<E>) -> Http.Res:
593  match error:
594    case Framework{status, allow}:
595      match status:
596        case 400:
597          problem(400, "Bad Request", allow)
598        case 404:
599          problem(404, "Not Found", allow)
600        case 405:
601          problem(405, "Method Not Allowed", allow)
602        case 415:
603          problem(415, "Unsupported Media Type", allow)
604        case _:
605          minimal()
606    case _:
607      minimal()
608
609def default.mapper(~S: Type, ~K: Data, ~E: Data, state: S, config: K, error: Error<E>) -> IO(S & Result<&2, &1, Error<E>, Http.Res>):
610  IO.pure(S & Result<&2, &1, Error<E>, Http.Res>, (state, Done{default.error(~E, error)}))
611
612def mapped.checked(~S: Type, ~H: Data, state: S, entered: List<&2, Policy<H>>, stopped: Bool, result: Result<&2, &1, ResponseError, Http.Res>) -> Completion<S, H>:
613  match result:
614    case Fail{error}:
615      Completed{state, minimal(), entered, True{}, True{}}
616    case Done{response}:
617      Completed{state, response, entered, True{}, stopped}
618
619def mapped.result(~S: Type, ~E: Data, ~H: Data, entered: List<&2, Policy<H>>, stopped: Bool, result: S & Result<&2, &1, Error<E>, Http.Res>) -> Completion<S, H>:
620  (state, outcome) = result
621  match outcome:
622    case Fail{error}:
623      Completed{state, minimal(), entered, True{}, True{}}
624    case Done{response}:
625      mapped.checked(~S, ~H, state, entered, stopped, validate(response))
626
627def failed(~S: Type, ~K: Data, ~E: Data, ~H: Data,
628  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
629  mapped: Bool, state: S, config: K, error: Error<E>, entered: List<&2, Policy<H>>, stopped: Bool
630) -> IO(Completion<S, H>):
631  match mapped:
632    case True{}:
633      IO.pure(Completion<S, H>, Completed{state, minimal(), entered, True{}, True{}})
634    case False{}:
635      do IO<Completion<S, H>>:
636        result : S & Result<&2, &1, Error<E>, Http.Res> <- mapper(state, config, error)
637        return mapped.result(~S, ~E, ~H, entered, stopped, result)
638
639def result(~S: Type, ~K: Data, ~E: Data, ~H: Data,
640  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
641  config: K, entered: List<&2, Policy<H>>, outcome: S & Result<&2, &1, Error<E>, Http.Res>
642) -> IO(Completion<S, H>):
643  (state, response) = outcome
644  match response:
645    case Fail{error}:
646      failed(~S, ~K, ~E, ~H, ~mapper, False{}, state, config, error, entered, False{})
647    case Done{res}:
648      IO.pure(Completion<S, H>, Completed{state, res, entered, False{}, False{}})
649
650def before.next(~S: Type, ~E: Data, step: Step<S, E>, next: S -> IO(Step<S, E>)) -> IO(Step<S, E>):
651  match step:
652    case Continue{state}:
653      next(state)
654    case Early{state, response}:
655      IO.pure(Step<S, E>, Early{state, response})
656    case Reject{state, error}:
657      IO.pure(Step<S, E>, Reject{state, error})
658
659def before(~S: Type, ~K: Data, ~E: Data, ~H: Data,
660  ~hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
661  hooks: List<&2, H>, state: S, +config: K, +meta: Metadata
662) -> IO(Step<S, E>):
663  match hooks:
664    case Nil{}:
665      IO.pure(Step<S, E>, Continue{state})
666    case h <> rest:
667      do IO<Step<S, E>>:
668        step : Step<S, E> <- hook(h, state, config, meta)
669        before.next(~S, ~E, step, s => before(~S, ~K, ~E, ~H, ~hook, rest, s, config, meta))
670
671def transformed(~S: Type, ~K: Data, ~E: Data, ~H: Data,
672  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
673  config: K, entered: List<&2, Policy<H>>, mapped: Bool, outcome: S & Result<&2, &1, Error<E>, Http.Res>
674) -> IO(Completion<S, H>):
675  (state, response) = outcome
676  match response:
677    case Fail{error}:
678      failed(~S, ~K, ~E, ~H, ~mapper, mapped, state, config, error, entered, True{})
679    case Done{res}:
680      IO.pure(Completion<S, H>, Completed{state, res, entered, mapped, False{}})
681
682def transforms(~S: Type, ~K: Data, ~E: Data, ~H: Data,
683  ~hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
684  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
685  hooks: List<&2, H>, +config: K, completion: Completion<S, H>
686) -> IO(Completion<S, H>):
687  match hooks:
688    case Nil{}:
689      IO.pure(Completion<S, H>, completion)
690    case h <> rest:
691      match completion:
692        case Completed{state, response, entered, mapped, True{}}:
693          IO.pure(Completion<S, H>, Completed{state, response, entered, mapped, True{}})
694        case Completed{state, response, +entered, +mapped, False{}}:
695          do IO<Completion<S, H>>:
696            changed : S & Result<&2, &1, Error<E>, Http.Res> <- hook(h, state, config, response)
697            next : Completion<S, H> <- transformed(~S, ~K, ~E, ~H, ~mapper, config, entered, mapped, changed)
698            transforms(~S, ~K, ~E, ~H, ~hook, ~mapper, rest, config, next)
699
700def entered.step(~S: Type, ~K: Data, ~E: Data, ~H: Data,
701  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
702  config: K, entered: List<&2, Policy<H>>, body: Http.Body, step: Step<S, E>, next: S -> List<&2, Policy<H>> -> Http.Body -> IO(Completion<S, H>)
703) -> IO(Completion<S, H>):
704  match step:
705    case Continue{state}:
706      next(state, entered, body)
707    case Early{state, response}:
708      IO.pure(Completion<S, H>, Completed{state, response, entered, False{}, False{}})
709    case Reject{state, error}:
710      failed(~S, ~K, ~E, ~H, ~mapper, False{}, state, config, error, entered, False{})
711
712def scopes(~S: Type, ~K: Data, ~E: Data, ~H: Data,
713  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
714  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
715  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
716  policies: List<&2, Policy<H>>, state: S, +config: K, +meta: Metadata, entered: List<&2, Policy<H>>, body: Http.Body,
717  next: S -> List<&2, Policy<H>> -> Http.Body -> IO(Completion<S, H>)
718) -> IO(Completion<S, H>):
719  match policies:
720    case Nil{}:
721      next(state, entered, body)
722    case Policy{+id, +hooks, +unwind} <> rest:
723      scope = {Policy{id, hooks, unwind} : Policy<H>}
724      do IO<Completion<S, H>>:
725        step : Step<S, E> <- before(~S, ~K, ~E, ~H, ~before_hook, hooks, state, config, meta)
726        inner : Completion<S, H> <- entered.step(~S, ~K, ~E, ~H, ~mapper, config, scope <> entered, body, step, s => ids => b => scopes(~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~mapper, rest, s, config, meta, ids, b, next))
727        transforms(~S, ~K, ~E, ~H, ~after_hook, ~mapper, unwind, config, inner)
728
729def finish.checked(~S: Type, ~K: Data, ~E: Data, ~H: Data,
730  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
731  state: S, config: K, entered: List<&2, Policy<H>>, mapped: Bool, stopped: Bool, checked: Result<&2, &1, ResponseError, Http.Res>
732) -> IO(Completion<S, H>):
733  match checked:
734    case Done{response}:
735      IO.pure(Completion<S, H>, Completed{state, response, entered, mapped, stopped})
736    case Fail{error}:
737      failed(~S, ~K, ~E, ~H, ~mapper, mapped, state, config, ResponseInvalid{}, entered, True{})
738
739def finish(~S: Type, ~K: Data, ~E: Data, ~H: Data,
740  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
741  config: K, completion: Completion<S, H>
742) -> IO(Completion<S, H>):
743  Completed{state, response, entered, mapped, stopped} = completion
744  finish.checked(~S, ~K, ~E, ~H, ~mapper, state, config, entered, mapped, stopped, validate(response))
745
746def policies.tail(~H: Data, policies: List<&2, Policy<H>>) -> List<&2, Policy<H>>:
747  match policies:
748    case Nil{}:
749      Nil{}
750    case root <> rest:
751      rest
752
753
754def endpoint(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
755  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
756  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
757  action: A, state: S, +config: K, selected: Match<Policy<H>>, meta: Metadata, entered: List<&2, Policy<H>>, body: Http.Body
758) -> IO(Completion<S, H>):
759  Metadata{method, target, headers} = meta
760  do IO<Completion<S, H>>:
761    outcome : S & Result<&2, &1, Error<E>, Http.Res> <- run(action, state, config, selected, Http.Req{method, target, headers, body})
762    result(~S, ~K, ~E, ~H, ~mapper, config, entered, outcome)
763
764def framework(~S: Type, ~K: Data, ~E: Data, ~H: Data,
765  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
766  +status: U32, allow: String, state: S, config: K, entered: List<&2, Policy<H>>, body: Http.Body
767) -> IO(Completion<S, H>):
768  match status:
769    case 204:
770      IO.pure(Completion<S, H>, Completed{state, Http.Res{204, Http.set(Http.empty(), "allow", allow), Http.from_string("")}, entered, False{}, False{}})
771    case _:
772      failed(~S, ~K, ~E, ~H, ~mapper, False{}, state, config, Framework{status, allow}, entered, False{})
773
774def choice(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
775  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
776  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
777  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
778  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
779  selected: Router.Choice<Endpoint<A, Policy<H>>>, target: Target.Target,
780  groups: List<&2, GroupPlan<Policy<H>>>, state: S, +config: K, +meta: Metadata, entered: List<&2, Policy<H>>, body: Http.Body
781) -> IO(Completion<S, H>):
782  match selected:
783    case Router.NotFound{}:
784      framework(~S, ~K, ~E, ~H, ~mapper, 404, "", state, config, entered, body)
785    case Router.MethodMissing{ids, allow}:
786      policies = groups.prepared(~Policy<H>, groups, ids)
787      scopes(~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~mapper, policies, state, config, meta, entered, body,
788        s => entered => b => framework(~S, ~K, ~E, ~H, ~mapper, 405, allow, s, config, entered, b))
789    case Router.Options{ids, allow}:
790      policies = groups.prepared(~Policy<H>, groups, ids)
791      scopes(~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~mapper, policies, state, config, meta, entered, body,
792        s => entered => b => framework(~S, ~K, ~E, ~H, ~mapper, 204, allow, s, config, entered, b))
793    case Router.Found{Endpoint{action, +policies}, params, ids, method, pattern}:
794      selected = {Match{target, params, ids, method, pattern, policies} : Match<Policy<H>>}
795      scopes(~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~mapper, policies.tail(~H, policies), state, config, meta, entered, body,
796        s => entered => b => endpoint(~A, ~S, ~K, ~E, ~H, ~run, ~mapper, action, s, config, selected, meta, entered, b))
797
798def metadata.authority(authority: Maybe<&2, String>, meta: Metadata) -> Metadata:
799  match authority:
800    case None{}:
801      meta
802    case Some{host}:
803      Metadata{method, target, headers} = meta
804      Metadata{method, target, Http.set(headers, "host", host)}
805
806def resolved(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
807  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
808  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
809  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
810  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
811  outcome: Result<&2, &2, Router.Error, Router.Resolved<Endpoint<A, Policy<H>>>>,
812  groups: List<&2, GroupPlan<Policy<H>>>, state: S, config: K, meta: Metadata, entered: List<&2, Policy<H>>, body: Http.Body
813) -> IO(Completion<S, H>):
814  match outcome:
815    case Fail{error}:
816      framework(~S, ~K, ~E, ~H, ~mapper, 400, "", state, config, entered, body)
817    case Done{Router.Resolved{+target, selected}}:
818      Target.Target{segments, query, authority, star} = target
819      choice(~A, ~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~run, ~mapper, selected, target, groups, state, config, metadata.authority(authority, meta), entered, body)
820
821def dispatch.application(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
822  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
823  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
824  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
825  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
826  app: Application<A, K, Policy<H>>, state: S, req: Http.Req
827) -> IO(Completion<S, H>):
828  Application{table, +config, root, groups} = app
829  Http.Req{+method, +path, +headers, body} = req
830  +meta = {Metadata{method, path, headers} : Metadata}
831  do IO<Completion<S, H>>:
832    completion : Completion<S, H> <- scopes(~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~mapper, [root], state, config, meta, Nil{}, body,
833      s => entered => b => resolved(~A, ~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~run, ~mapper,
834        Router.resolve(~Endpoint<A, Policy<H>>, table, method, path), groups, s, config, meta, entered, b))
835    finish(~S, ~K, ~E, ~H, ~mapper, config, completion)
836
837def access.disabled(~K: Data, config: K) -> Notices.Access:
838  Notices.Disabled{}
839
840def notice.ignore(~N: Data, ~H: Data, ~E: Data, observer: N, policy: Policy<H>, notice: Notices.Notice) -> IO(Result<&2, &2, E, Unit>):
841  IO.pure(Result<&2, &2, E, Unit>, Done{Unit{}})
842
843def notice.error.ignore(~N: Data, ~E: Data, observer: N, error: E) -> IO(Unit):
844  IO.pure(Unit, Unit{})
845
846def notice.result(~N: Data, ~E: Data, ~report: N -> E -> IO(Unit), observer: N, result: Result<&2, &2, E, Unit>) -> IO(Unit):
847  match result:
848    case Done{unit}: IO.pure(Unit, Unit{})
849    case Fail{error}: report(observer, error)
850
851def notices(~N: Data, ~H: Data, ~E: Data,
852  ~notify: N -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
853  ~report: N -> E -> IO(Unit),
854  entered: List<&2, Policy<H>>, +observer: N, +notice: Notices.Notice
855) -> IO(Unit):
856  match entered:
857    case Nil{}: IO.pure(Unit, Unit{})
858    case policy <> rest:
859      do IO<Unit>:
860        result : Result<&2, &2, E, Unit> <- notify(observer, policy, notice)
861        notice.result(~N, ~E, ~report, observer, result)
862        notices(~N, ~H, ~E, ~notify, ~report, rest, observer, notice)
863
864def dispatch.completed(~S: Type, ~N: Data, ~H: Data, ~E: Data,
865  ~notify: N -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
866  ~report: N -> E -> IO(Unit),
867  observer: N, access: Notices.Access, started: Time.Duration, completion: Completion<S, H>
868) -> IO(Completion<S, H>):
869  Completed{state, Http.Res{+status, headers, body}, +entered, +mapped, +stopped} = completion
870  do IO<Completion<S, H>>:
871    ended : Time.Duration <- Time.mono()
872    +notice : Notices.Notice = Notices.Notice{status, Time.Duration.sub(ended, started), mapped, stopped, Notices.ApplicationOnly{}}
873    notices(~N, ~H, ~E, ~notify, ~report, entered, observer, notice)
874    Notices.access(access, notice)
875    return Completed{state, Http.Res{status, headers, body}, entered, mapped, stopped}
876
877# The same application pipeline is used live; only this boundary emits direct notices.
878def dispatch(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data, ~N: Data,
879  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
880  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
881  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
882  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
883  ~notify: N -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
884  ~report: N -> E -> IO(Unit),
885  observer: N, access: Notices.Access, app: Application<A, K, Policy<H>>, state: S, req: Http.Req
886) -> IO(Completion<S, H>):
887  do IO<Completion<S, H>>:
888    started : Time.Duration <- Time.mono()
889    completion : Completion<S, H> <- dispatch.application(~A, ~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~run, ~mapper, app, state, req)
890    dispatch.completed(~S, ~N, ~H, ~E, ~notify, ~report, observer, access, started, completion)
891
892# The wrapper owns both pipes until its admitted operation really returns.
893# Only disposable HTTP packed buffers cross them, never external handles.
894type BufferedRequest is Data:
895  BufferedRequest{metadata: Metadata, body: Chan(Http.Body), response: Chan(Http.Res)}
896
897type ApplicationReport<-H: Data> is Data:
898  Dispatched{entered: List<&2, Policy<H>>, mapped: Bool, stopped: Bool}
899  CapacityRejected{}
900
901type ServingContext<-A: Data, -S: Type, -K: Data, -H: Data> is Data:
902  ServingContext{application: Application<A, K, Policy<H>>,
903    owner: Context<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>}
904
905type Server<-A: Data, -S: Type, -K: Data, -H: Data> is Type:
906  Server{transport: Http.Server<ServingContext<A, S, K, H>>,
907    owner: Owner<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>}
908
909type StartupError is Data:
910  RegistrationFailed{error: RegistrationError}
911  TransportFailed{error: Http.StartupError}
912  EmptyBundles{}
913
914type ServerExit<-A: Data, -S: Type, -K: Data, -H: Data> is Type:
915  ServerExit{transport: Http.ServerExit, application: OwnerClose<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>}
916
917def server.config(port: U32) -> Http.ServerConfig:
918  Http.ServerConfig{"127.0.0.1", port, 1048576, 65536, 128, 128, 128, 5000, 30000, 30000, 30000}
919
920def server.bundles.cons(~S: Type, resource: S, counted: List<S> & Nat) -> List<S> & Nat:
921  (returned, count) = counted
922  (resource <> returned, 1n+count)
923
924def server.bundles(~S: Type, resources: List<S>) -> List<S> & Nat:
925  match resources:
926    case Nil{}: (Nil{}, 0n)
927    case resource <> rest:
928      server.bundles.cons(~S, resource, server.bundles(~S, rest))
929
930def server.work.completed(~S: Type, ~H: Data, response_channel: Chan(Http.Res),
931  completion: Completion<S, H>
932) -> IO(S & Result<&2, &2, Unit, ApplicationReport<H>>):
933  Completed{state, response, entered, mapped, stopped} = completion
934  do IO<S & Result<&2, &2, Unit, ApplicationReport<H>>>:
935    sent : Bool <- Chan.send(Http.Res, response_channel, response)
936    return (state, Done{Dispatched{entered, mapped, stopped}})
937
938def server.work(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
939  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
940  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
941  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
942  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
943  state: S, app: Application<A, K, Policy<H>>, input: BufferedRequest
944) -> IO(S & Result<&2, &2, Unit, ApplicationReport<H>>):
945  BufferedRequest{Metadata{method, target, headers}, body_channel, response_channel} = input
946  do IO<S & Result<&2, &2, Unit, ApplicationReport<H>>>:
947    body : Http.Body <- receive(Http.Body, body_channel)
948    completion : Completion<S, H> <- dispatch.application(~A, ~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~run, ~mapper,
949      app, state, Http.Req{method, target, headers, body})
950    server.work.completed(~S, ~H, response_channel, completion)
951
952type NoticePayload<-K: Data, -H: Data> is Data:
953  NoticePayload{config: K, started: Time.Duration, application: ApplicationReport<H>}
954
955def server.notified(~K: Data, ~H: Data, ~E: Data,
956  ~notify: K -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
957  ~report: K -> E -> IO(Unit), ~access: K -> Notices.Access,
958  ~done: TransportCompletion.Completion<ApplicationReport<H>> -> IO(Unit),
959  +config: K, +notice: Notices.Notice, application: ApplicationReport<H>,
960  status: U32, outcome: TransportCompletion.Outcome
961) -> IO(Unit):
962  match application:
963    case Dispatched{+entered, +mapped, +stopped}:
964      do IO<Unit>:
965        notices(~K, ~H, ~E, ~notify, ~report, entered, config, notice)
966        Notices.access(access(config), notice)
967        done(TransportCompletion.Completion{Dispatched{entered, mapped, stopped}, status, outcome})
968    case CapacityRejected{}:
969      do IO<Unit>:
970        Notices.access(access(config), notice)
971        done(TransportCompletion.Completion{CapacityRejected{}, status, outcome})
972
973def server.completed.report(~K: Data, ~H: Data, ~E: Data,
974  ~notify: K -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
975  ~report: K -> E -> IO(Unit), ~access: K -> Notices.Access,
976  ~done: TransportCompletion.Completion<ApplicationReport<H>> -> IO(Unit),
977  config: K, duration: Time.Duration, application: ApplicationReport<H>, +status: U32, +outcome: TransportCompletion.Outcome
978) -> IO(Unit):
979  match application:
980    case Dispatched{+entered, +mapped, +stopped}:
981      server.notified(~K, ~H, ~E, ~notify, ~report, ~access, ~done, config,
982        Notices.Notice{status, duration, mapped, stopped, Notices.Transport{outcome}}, Dispatched{entered, mapped, stopped}, status, outcome)
983    case CapacityRejected{}:
984      server.notified(~K, ~H, ~E, ~notify, ~report, ~access, ~done, config,
985        Notices.Notice{status, duration, False{}, False{}, Notices.Transport{outcome}}, CapacityRejected{}, status, outcome)
986
987def server.completed(~K: Data, ~H: Data, ~E: Data,
988  ~notify: K -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
989  ~report: K -> E -> IO(Unit), ~access: K -> Notices.Access,
990  ~done: TransportCompletion.Completion<ApplicationReport<H>> -> IO(Unit),
991  completion: TransportCompletion.Completion<NoticePayload<K, H>>
992) -> IO(Unit):
993  TransportCompletion.Completion{NoticePayload{config, started, +application}, +status, +outcome} = completion
994  do IO<Unit>:
995    ended : Time.Duration <- Time.mono()
996    server.completed.report(~K, ~H, ~E, ~notify, ~report, ~access, ~done, config, Time.Duration.sub(ended, started), application, status, outcome)
997
998def server.response(~K: Data, ~H: Data,
999  ~done: TransportCompletion.Completion<NoticePayload<K, H>> -> IO(Unit),
1000  config: K, started: Time.Duration, outcome: Outcome<Unit, ApplicationReport<H>>, +body: Chan(Http.Body), +response: Chan(Http.Res)
1001) -> IO(Http.Reply):
1002  match outcome:
1003    case Success{report}:
1004      do IO<Http.Reply>:
1005        result : Http.Res <- receive(Http.Res, response)
1006        Chan.close(Http.Body, body)
1007        Chan.close(Http.Res, response)
1008        return Http.reply.complete(~NoticePayload<K, H>, ~done, NoticePayload{config, started, report}, result)
1009    case _:
1010      do IO<Http.Reply>:
1011        # Closing alone retains buffered values in Base. Consume the rejected
1012        # body before closing; the single room1 send has already completed.
1013        discarded : Http.Body <- receive(Http.Body, body)
1014        Chan.close(Http.Body, body)
1015        Chan.close(Http.Res, response)
1016        return Http.reply.complete(~NoticePayload<K, H>, ~done, NoticePayload{config, started, CapacityRejected{}},
1017          Http.Res{503, Http.set(Http.empty(), "connection", "close"), Http.from_string("")})
1018
1019def server.handle(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1020  ~done: TransportCompletion.Completion<NoticePayload<K, H>> -> IO(Unit),
1021  context: ServingContext<A, S, K, H>, req: Http.Req
1022) -> IO(Http.Reply):
1023  ServingContext{+app, owner} = context
1024  Application{table, config, root, groups} = app
1025  Http.Req{method, target, headers, body} = req
1026  do IO<Http.Reply>:
1027    started : Time.Duration <- Time.mono()
1028    +body_channel : Chan(Http.Body) <- Chan.new(Http.Body, 1)
1029    +response_channel : Chan(Http.Res) <- Chan.new(Http.Res, 1)
1030    sent : Bool <- Chan.send(Http.Body, body_channel, body)
1031    outcome : Outcome<Unit, ApplicationReport<H>> <- call(S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>,
1032      owner, app, BufferedRequest{Metadata{method, target, headers}, body_channel, response_channel})
1033    server.response(~K, ~H, ~done, config, started, outcome, body_channel, response_channel)
1034
1035
1036def server.started(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1037  ~work: S -> Application<A, K, Policy<H>> -> BufferedRequest -> IO(S & Result<&2, &2, Unit, ApplicationReport<H>>),
1038  ~destroy: S -> IO(Unit),
1039  +context: Context<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>,
1040  room: U32, capacity: Nat,
1041  started: List<S> & Result<&2, &1, Http.StartupError, Http.Server<ServingContext<A, S, K, H>>>
1042) -> IO(Result<&2, &1, StartupError, Server<A, S, K, H>>):
1043  Context{+messages} = context
1044  (resources, result) = started
1045  match result:
1046    case Fail{error}:
1047      do IO<Result<&2, &1, StartupError, Server<A, S, K, H>>>:
1048        dispose(~S, ~destroy, resources)
1049        Chan.close(Message<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>, messages)
1050        return Fail{TransportFailed{error}}
1051    case Done{transport}:
1052      do IO<Result<&2, &1, StartupError, Server<A, S, K, H>>>:
1053        finished : Chan(Unit) <- IO.fork(Unit, owner(~S, ~Application<A, K, Policy<H>>, ~BufferedRequest, ~Unit, ~ApplicationReport<H>,
1054          ~work, ~destroy, messages, room, Waiting{resources, Counts{capacity, 0n, 0n, 0n}}))
1055        return Done{Server{transport, Owner{Context{messages}, finished}}}
1056
1057def server.prepared(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1058  ~work: S -> Application<A, K, Policy<H>> -> BufferedRequest -> IO(S & Result<&2, &2, Unit, ApplicationReport<H>>),
1059  ~destroy: S -> IO(Unit), app: Application<A, K, Policy<H>>, +config: Http.ServerConfig, resources: List<S> & Nat
1060) -> IO(Result<&2, &1, StartupError, Server<A, S, K, H>>):
1061  Http.ServerConfig{host, port, body, headers, connections, requests, +buffered, hm, bm, im, wm} = config
1062  (bundles, capacity) = resources
1063  match capacity:
1064    case 0n:
1065      do IO<Result<&2, &1, StartupError, Server<A, S, K, H>>>:
1066        dispose(~S, ~destroy, bundles)
1067        return Fail{EmptyBundles{}}
1068    case 1n+ +rest:
1069      do IO<Result<&2, &1, StartupError, Server<A, S, K, H>>>:
1070        messages : Chan(Message<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>) <-
1071          Chan.new(Message<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>, buffered)
1072        +context : Context<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>> = Context{messages}
1073        started : List<S> & Result<&2, &1, Http.StartupError, Http.Server<ServingContext<A, S, K, H>>> <-
1074          Http.server.start(~ServingContext<A, S, K, H>, ~List<S>, ServingContext{app, context}, bundles, config)
1075        server.started(~A, ~S, ~K, ~H, ~work, ~destroy, context, buffered, 1n+rest, started)
1076
1077def server.valid(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1078  ~work: S -> Application<A, K, Policy<H>> -> BufferedRequest -> IO(S & Result<&2, &2, Unit, ApplicationReport<H>>),
1079  ~destroy: S -> IO(Unit), valid: Bool, app: Application<A, K, Policy<H>>, config: Http.ServerConfig, resources: List<S>
1080) -> IO(Result<&2, &1, StartupError, Server<A, S, K, H>>):
1081  match valid:
1082    case False{}:
1083      do IO<Result<&2, &1, StartupError, Server<A, S, K, H>>>:
1084        dispose(~S, ~destroy, resources)
1085        return Fail{TransportFailed{Http.InvalidConfig{}}}
1086    case True{}:
1087      server.prepared(~A, ~S, ~K, ~H, ~work, ~destroy, app, config, server.bundles(~S, resources))
1088
1089def server.registered(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1090  ~work: S -> Application<A, K, Policy<H>> -> BufferedRequest -> IO(S & Result<&2, &2, Unit, ApplicationReport<H>>),
1091  ~destroy: S -> IO(Unit), registration: Result<&2, &2, RegistrationError, Application<A, K, Policy<H>>>,
1092  +config: Http.ServerConfig, resources: List<S>
1093) -> IO(Result<&2, &1, StartupError, Server<A, S, K, H>>):
1094  match registration:
1095    case Fail{error}:
1096      do IO<Result<&2, &1, StartupError, Server<A, S, K, H>>>:
1097        dispose(~S, ~destroy, resources)
1098        return Fail{RegistrationFailed{error}}
1099    case Done{app}:
1100      server.valid(~A, ~S, ~K, ~H, ~work, ~destroy, Http.server.config.valid(config), app, config, resources)
1101
1102def server.start(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
1103  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
1104  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1105  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1106  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1107  ~destroy: S -> IO(Unit),
1108  registration: Result<&2, &2, RegistrationError, Application<A, K, Policy<H>>>,
1109  config: Http.ServerConfig, resources: List<S>
1110) -> IO(Result<&2, &1, StartupError, Server<A, S, K, H>>):
1111  server.registered(~A, ~S, ~K, ~H,
1112    ~(state => app => input => server.work(~A, ~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~run, ~mapper, state, app, input)),
1113    ~destroy, registration, config, resources)
1114
1115def server.control.result(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1116  controlled: Http.Server<ServingContext<A, S, K, H>> & Http.ServerControl,
1117  owner: Owner<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>
1118) -> Server<A, S, K, H> & Http.ServerControl:
1119  (returned, control) = controlled
1120  (Server{returned, owner}, control)
1121
1122def server.control(~A: Data, ~S: Type, ~K: Data, ~H: Data, server: Server<A, S, K, H>) -> Server<A, S, K, H> & Http.ServerControl:
1123  Server{transport, owner} = server
1124  server.control.result(~A, ~S, ~K, ~H, Http.server.control(~ServingContext<A, S, K, H>, transport), owner)
1125
1126def server.observer.result(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1127  observed: Http.Server<ServingContext<A, S, K, H>> & Http.ServerStats,
1128  owner: Owner<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>
1129) -> Server<A, S, K, H> & Http.ServerStats:
1130  (returned, stats) = observed
1131  (Server{returned, owner}, stats)
1132
1133def server.observer(~A: Data, ~S: Type, ~K: Data, ~H: Data, server: Server<A, S, K, H>) -> Server<A, S, K, H> & Http.ServerStats:
1134  Server{transport, owner} = server
1135  server.observer.result(~A, ~S, ~K, ~H, Http.server.observer(~ServingContext<A, S, K, H>, transport), owner)
1136
1137def server.dependencies(~A: Data, ~S: Type, ~K: Data, ~H: Data, server: Server<A, S, K, H>
1138) -> Server<A, S, K, H> & Context<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>:
1139  Server{transport, Owner{+context, finished}} = server
1140  (Server{transport, Owner{context, finished}}, context)
1141
1142def server.run(~A: Data, ~S: Type, ~K: Data, ~H: Data, ~E: Data,
1143  ~done: TransportCompletion.Completion<ApplicationReport<H>> -> IO(Unit),
1144  ~observe: U32 -> TransportCompletion.Outcome -> IO(Unit),
1145  ~notify: K -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
1146  ~report: K -> E -> IO(Unit), ~access: K -> Notices.Access,
1147  server: Server<A, S, K, H>
1148) -> IO(ServerExit<A, S, K, H>):
1149  Server{transport, owner} = server
1150  do IO<ServerExit<A, S, K, H>>:
1151    exited : Http.ServerExit <- Http.server.run(~ServingContext<A, S, K, H>,
1152      ~(context => req => server.handle(~A, ~S, ~K, ~H,
1153        ~(completion => server.completed(~K, ~H, ~E, ~notify, ~report, ~access, ~done, completion)), context, req)),
1154      ~(context => status => outcome => observe(status, outcome)), transport)
1155    closed : OwnerClose<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>> <- close(S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>, owner)
1156    return ServerExit{exited, closed}
1157
1158# Without starting the run loop, the caller still explicitly drains transport
1159# and joins application cleanup. A stop request alone is never completion.
1160def server.close(~A: Data, ~S: Type, ~K: Data, ~H: Data, server: Server<A, S, K, H>) -> IO(ServerExit<A, S, K, H>):
1161  Server{transport, owner} = server
1162  do IO<ServerExit<A, S, K, H>>:
1163    counts : Http.ServerCounts <- Http.server.close(~ServingContext<A, S, K, H>, transport)
1164    closed : OwnerClose<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>> <- close(S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>, owner)
1165    return ServerExit{Http.ServerStopped{counts}, closed}
1166
1167def serve.started(~A: Data, ~S: Type, ~K: Data, ~H: Data, ~E: Data,
1168  ~done: TransportCompletion.Completion<ApplicationReport<H>> -> IO(Unit),
1169  ~observe: U32 -> TransportCompletion.Outcome -> IO(Unit),
1170  ~notify: K -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
1171  ~report: K -> E -> IO(Unit), ~access: K -> Notices.Access,
1172  started: Result<&2, &1, StartupError, Server<A, S, K, H>>
1173) -> IO(Result<&2, &1, StartupError, ServerExit<A, S, K, H>>):
1174  match started:
1175    case Fail{error}: IO.pure(Result<&2, &1, StartupError, ServerExit<A, S, K, H>>, Fail{error})
1176    case Done{server}:
1177      do IO<Result<&2, &1, StartupError, ServerExit<A, S, K, H>>>:
1178        exited : ServerExit<A, S, K, H> <- server.run(~A, ~S, ~K, ~H, ~E, ~done, ~observe, ~notify, ~report, ~access, server)
1179        return Done{exited}
1180
1181# Owners that need readiness/control use server.start then server.run instead.
1182def serve(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
1183  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
1184  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1185  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1186  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1187  ~destroy: S -> IO(Unit),
1188  ~done: TransportCompletion.Completion<ApplicationReport<H>> -> IO(Unit),
1189  ~observe: U32 -> TransportCompletion.Outcome -> IO(Unit),
1190  ~notify: K -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
1191  ~report: K -> E -> IO(Unit), ~access: K -> Notices.Access,
1192  registration: Result<&2, &2, RegistrationError, Application<A, K, Policy<H>>>,
1193  config: Http.ServerConfig, resources: List<S>
1194) -> IO(Result<&2, &1, StartupError, ServerExit<A, S, K, H>>):
1195  do IO<Result<&2, &1, StartupError, ServerExit<A, S, K, H>>>:
1196    started : Result<&2, &1, StartupError, Server<A, S, K, H>> <-
1197      server.start(~A, ~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~run, ~mapper, ~destroy, registration, config, resources)
1198    serve.started(~A, ~S, ~K, ~H, ~E, ~done, ~observe, ~notify, ~report, ~access, started)