Skip to main content

max / quasi

7.1 KB · 154 lines History Blame Raw
1 # quasi
2
3 quasicoherent: the stack every Make Creative app is built on. `quasi` for short,
4 and that is what you type.
5
6 A quasi-coherent sheaf is determined by local module data on an affine cover and
7 glues, with no finiteness demanded of the pieces. That is the architecture: one
8 description per host context, agreeing on overlaps, renderers free to be unalike.
9
10 ## What it is
11
12 ```
13 request (path + params)
14 -> router this repo, imports no host crate
15 -> description makeover-layout
16 -> renderer webview | tui | egui
17 -> host adapter axum route | Tauri protocol | wry | direct call
18 ```
19
20 The router answers with what a screen *is*. The host decides how that becomes
21 pixels. One codebase reaches desktop, iOS, a terminal, egui, and a hosted web
22 app, because the host is the boundary that stops mattering.
23
24 ## What it owns
25
26 Seams, not vendors. quasi owns the boundaries between code we own and the major
27 dependencies underneath it, and the test for admitting one is:
28
29 **Name two implementations that exist or are already committed to.** A boundary
30 with one implementation forever is not a boundary, it is an indirection.
31
32 By that test, in scope: the host (Tauri, wry, axum, direct call), the renderer
33 (webview, tui, egui, via `makeover-layout`), database configuration and
34 migration across SQLite and Postgres, and eventually a shared session and auth
35 layer, which the server has and the desktop apps do not.
36
37 Out of scope, deliberately: `tokio`, `serde`, `tracing`, `chrono`, `uuid`,
38 `thiserror`. Ubiquitous, stable, and with no second implementation anyone would
39 adopt, so wrapping them costs a translation tax and returns nothing.
40
41 Also out of scope: anything whose value *is* its API surface. `sqlx`'s
42 compile-time-checked query macros are the reason to use `sqlx`, so quasi owns
43 which driver is configured and how migrations run, and queries stay written
44 against the driver directly.
45
46 The two stores are not one query layer, and quasi does not pretend otherwise.
47 Embedded is `rusqlite` and synchronous; hosted Postgres is `sqlx` and async. A
48 stack claiming a single query layer would have the scaffolder generate the wrong
49 one.
50
51 Boundaries already owned elsewhere stay where they are and are referenced rather
52 than absorbed: `makeover` and `makeover-geometry` for colour and spacing,
53 `synckit` for sync, `s3-storage` for blobs, `docengine` for rendered content,
54 `tagtree` for tags. One repo per project. quasi names the stack and fills the
55 seams that have no owner.
56
57 ## Layout
58
59 - `crates/quasi-router/` — the host-agnostic router. The keystone.
60 - `crates/quasi-http/` — the seam the http-shaped hosts share: decoding,
61 status mapping, the htmx contract, the `Serves` trait.
62 - `crates/quasi-axum/` — the axum host adapter.
63 - `crates/quasi-tauri/` — the Tauri custom-protocol host adapter.
64 - `crates/quasi-webview/` — the webview renderer: a screen in, an htmx document
65 out.
66 - `crates/quasi-tui/` — the terminal renderer: a screen in, cells in a ratatui
67 buffer out.
68 - `crates/quasi-immediate/` — the immediate-mode renderer: a screen in, an egui
69 frame out.
70 - `crates/quasi-basics/` — the first-party widget set: named assemblies of
71 primitives, shared across our apps.
72 - `crates/quasi-notifs/` — declared notification kinds: what a notification says
73 and what it is for, the configuration that follows from it, and the delivery
74 every host would otherwise re-answer.
75 - `crates/quasi-store/` — the embedded store's migration runner.
76 - `crates/quasi-bench/` — what a described screen costs, in time and in
77 allocations.
78 - `crates/quasi/` — the scaffolder binary, and `template/`, the app it writes.
79
80 Crates are added when their component starts, not up front.
81
82 ## Getting an app
83
84 ```
85 cargo run -p quasi -- new fieldnotes
86 cd fieldnotes
87 cargo test the screens, with no host
88 cargo run -p fieldnotes-server http://127.0.0.1:3000
89 cargo run -p fieldnotes-desktop a window
90 ```
91
92 What comes out is a workspace of three crates: `-core` holds the state, the
93 store and the described screens and imports no host crate; `-desktop` and
94 `-server` are a window and a listener over the same router. Both serve the same
95 screens and neither knows the other exists.
96
97 ## The parts
98
99 The router carries the contract in full: one address space where the verb
100 separates a read from a write, a screen tree composed from `makeover-layout`'s
101 vocabulary, responses that name the region they replace, and failures classified
102 so a host can turn one into a status code and another into a banner. It is sync,
103 because the two renderers shipping first call it inside a frame and an event
104 loop.
105
106 `quasi-http` is what the http-shaped hosts share. Decoding a query string and a
107 form body, mapping an error's class to a status, and naming a fragment's region
108 in an `HX-Retarget` header. The transport
109 is htmx 4, which is what an emitted document links and what the response headers
110 here are checked against. Nothing in it knows which host it is in, and nothing
111 in it is async.
112
113 `quasi-axum` is the hosted adapter. What is left in it is axum's own: it mounts
114 as a fallback, so ordinary axum routes merged in front keep serving the things a
115 description has no word for (static assets, health, file uploads), it reads the
116 body with a limit, and it makes the blocking hop the sync router needs.
117
118 `quasi-tauri` is the desktop and iOS adapter, a custom-protocol handler. The
119 window loads from the adapter's own scheme rather than from tauri's asset
120 protocol, which is what keeps every action a screen emits same-origin: no CORS,
121 nothing added to the CSP. A `passthrough` closure is its version of axum's
122 merged routes. There is no platform branch in it, because wry hands a handler
123 `<scheme>://localhost/<path>` on Linux, macOS, iOS and Windows alike.
124
125 Neither adapter emits markup. A `Serves` implementation supplies that, because
126 both serve HTML to a webview and generating it inside one would guarantee a
127 second copy. `quasi-webview` is that implementation: a screen in, an htmx
128 document out, with the transport entering in a single function.
129
130 `quasi-notifs` is a notification declared once. A kind carries what it says,
131 what it is for, whether it ships on and what its knobs are; the configuration
132 follows from the declaration, keys, postures, defaults and a described settings
133 pane, rather than being written beside it in three places. Delivery is a host
134 adapter of one method, because everything else a host would answer for itself
135 (consulting the kind's `enabled` key, suppressing a duplicate, staying quiet
136 about what came due while the app was shut, bounding the memory that takes) is
137 shared and belongs to nobody's renderer.
138
139 `quasi-store` is the embedded store's migration runner. SQL files embedded at
140 build time, applied once, recorded in a ledger with a checksum, so an applied
141 migration is immutable.
142
143 `quasi` is the scaffolder. Its template is real Rust and real manifests under
144 `crates/quasi/template/`, excluded from the workspace and embedded in the
145 binary, so it is readable and diffable rather than a liquid dialect that
146 compiles nowhere.
147
148 Design and sequencing live in the wiki note `quasi-overview`; the backlog is in
149 GoingsOn under project `quasicoherent`.
150
151 ## Licence
152
153 MIT.
154