jevhooks.git / web / src / README.md
1# Chapter 18: web/src, one page and the little it takes to serve it
2
3Seven files. Read them in this order and you have the whole site.
4
5**[content.rs](content.rs) is what the page says, as data.** The title and
6the description a link preview carries. The addresses it links to. The
7hook, twelve pixels by twelve, that sits in every title bar. And the four
8pictures, each a `Shot`: the file, a title, a sentence or two, the words
9for someone who cannot see it, and its real width and height. There is no
10markup in this file, on purpose: you can change what the site says without
11touching how it is drawn.
12
13**[hooks.rs](hooks.rs) is the map of hook points.** Claude Code raises
14thirty-three kinds of event a plugin can answer, and the page lists every
15one with what jevhooks does there: judged, heard, an idea, or nothing yet.
16The list is not typed out a second time. It is the plugin's own
17(`jevhooks_events::HookEvent`, chapter 8), the very code the mod and the
18daemon are compiled from, and "judged" and "heard" are read from its
19`role`. So the page cannot claim an event the plugin does not handle. What
20this file adds is where an event sits in a session, a sentence on what
21happens there, and the ideas somebody has written down.
22
23**[rules.rs](rules.rs) is the rules, drawn.** Chapter 12's rules are
24compiled into three small networks, and the page shows each one as a
25picture. Not a picture *of* them, drawn by hand beside the code, which
26would be wrong by the second change: `rete-draw` draws the very network
27the daemon runs, compiled into this Worker from the same crate. What this
28file adds is ten worked cases (`cargo test`, a force push, a turn that
29stopped early, ...). Each is an answer Jev could give, read through the
30plugin's own thresholds and run through the network, and how it ended is
31whatever the network said. Nobody typed "allow" next to `cargo test`.
32
33**[view.rs](view.rs) draws it.** `page(origin)` is a pure function: an
34address in, a string of HTML out. It fetches nothing, reads no clock and
35knows no visitor. That is what makes the page testable without a browser
36or a Cloudflare account: the tests call `page`, and read the string.
37
38**[lib.rs](lib.rs) is the Worker.** Every request passes through `fetch`,
39which does four things in order:
40
41```mermaid
42flowchart TD
43  R["a request"] --> G{"came to the canonical address,<br/>or a local one?"}
44  G -- "no, and it is a GET or HEAD" --> M["308 to the same path<br/>on hooks.lmjtfy.fun"]
45  G -- yes --> C{"the preview card?"}
46  C -- yes --> A["from the static assets"]
47  C -- no --> P["the router:<br/>the page, the icon, robots.txt,<br/>a picture, a shared file, or 404"]
48  M --> H["security headers on every answer"]
49  A --> H
50  P --> H
51  H --> L["one log line: route, method, status, time"]
52```
53
54**[log.rs](log.rs) is what gets written down**, and mostly what does not.
55A path is turned into one of nine `Route`s before anything is logged, so
56the log can say `GET shot -> 200 in 1 ms` and has nowhere to put an
57address, a query or a header. A path nobody serves is the one word
58`other`, never its own text.
59
60**[bin/card.rs](bin/card.rs) draws the link preview's card** (chapter 17's
61second aside) and prints it.
62
63> **Aside: a policy with no holes to forget.** Every answer carries a
64> Content Security Policy, the header that tells a browser what a page may
65> load. This site's says: its own files, and nothing else. No script from
66> anywhere else, no script written into the page, none built from a
67> string. It comes from `aldebaran-headers`, where a policy starts at
68> "nothing" and each allowance is a method you have to call by name
69> (this site calls two: `with_inline_styles`, for the stylesheet the shell
70> puts in the head, and `with_data_images`, because the dotted background
71> is a picture the shared script draws in your browser and hands to the
72> page as data; the first deploy left that one out, and the dots were
73> simply not there). A test builds the page and fails if an inline
74> `<script>` ever appears, because the browser would drop it without a
75> word.
76
77> **Try it.** `mise run test:web`, then break something:
78> give a `Shot` the wrong `height` in `content.rs` and
79> `each_picture_declares_its_real_size` reads the real one out of the SVG
80> and says so; put a second `loading="eager"` picture in and
81> `every_feature_has_its_picture` notices.
82
83## For the people who maintain it
84
85| Path | What |
86| --- | --- |
87| [content.rs](content.rs) | `NAME`, `TITLE`, `DESCRIPTION`, the links, `TRY_IT`, the install lines (`VERSION`, `install`, `release_key`, which reads `tools/release.pub`), the card's path and size, `HOOK`, `Shot` and the four of them (`SHOTS`), and `FILES`, the bundle the pictures are served from. |
88| [hooks.rs](hooks.rs) | `Phase`, `State`, `Hook`, `hook(event)` (an exhaustive match: a new event does not compile until it is placed), `all`, `count`, `BEYOND` (the two mod events used for model choice), and tests that hold "built" to the plugin's own roles. |
89| [rules.rs](rules.rs) | `run` (a network run to its end, with the rules that decided), `judgments` (the three, each with its cases as `<details>`, drawn by `rete_draw::rete`), and tests that hold each case's ending to the network's. |
90| [view.rs](view.rs) | `page`, `favicon`, the link preview (`jev_ui::preview::Preview`), and the page's tests. |
91| [page.css](page.css) | The site's own few rules, after the shared sheet: the lede, the picture's frame, the promise, the steps, the map's chips, a case, and the look of a drawn network (`.rete`; `rete-draw` writes classes and no styles). Tokens only. |
92| [lib.rs](lib.rs) | `fetch`, `gate`, `card`, the router, `policy`, `origin` (from `SITE_ORIGIN`), and the gate's and policy's tests. |
93| [log.rs](log.rs) | `Route` and its names; the note a missing card writes (`mise run web:build`). |
94| [bin/card.rs](bin/card.rs) | The card as SVG on stdout: `jev_ui::card::Chrome` at twice its base size, 1200 by 630, three lines in the window. |
95
96← Previous: [Chapter 17, web/](../) · Up: [web](../) · Next: nothing; you have read it all. Back to [the start](../../) →