toiljs 0.0.14 → 0.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/.babelrc +13 -13
  2. package/.gitattributes +2 -2
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
  4. package/.github/ISSUE_TEMPLATE/bug_report.yml +90 -90
  5. package/.github/ISSUE_TEMPLATE/config.yml +8 -8
  6. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
  7. package/.github/PULL_REQUEST_TEMPLATE.md +43 -43
  8. package/.github/changelog-config.json +45 -45
  9. package/.github/dependabot.yml +27 -27
  10. package/.github/workflows/ci.yml +191 -191
  11. package/.prettierrc.json +11 -11
  12. package/.vscode/settings.json +9 -9
  13. package/CHANGELOG.md +5 -5
  14. package/LICENSE +187 -187
  15. package/README.md +339 -315
  16. package/as-pect.asconfig.json +34 -34
  17. package/as-pect.config.js +65 -65
  18. package/assets/logo.svg +36 -36
  19. package/build/backend/.tsbuildinfo +1 -1
  20. package/build/cli/.tsbuildinfo +1 -1
  21. package/build/cli/index.js +2926 -191
  22. package/build/client/.tsbuildinfo +1 -1
  23. package/build/client/dev/devtools.d.ts +6 -0
  24. package/build/client/dev/devtools.js +442 -0
  25. package/build/client/dev/error-overlay.d.ts +9 -0
  26. package/build/client/dev/error-overlay.js +19 -4
  27. package/build/client/head/metadata.d.ts +3 -1
  28. package/build/client/head/metadata.js +8 -0
  29. package/build/client/index.d.ts +4 -4
  30. package/build/client/index.js +2 -2
  31. package/build/client/navigation/navigation.d.ts +2 -0
  32. package/build/client/navigation/navigation.js +9 -1
  33. package/build/client/navigation/prefetch.d.ts +1 -0
  34. package/build/client/navigation/prefetch.js +35 -0
  35. package/build/client/routing/Router.js +1 -1
  36. package/build/client/routing/hooks.js +6 -2
  37. package/build/client/routing/loader.d.ts +25 -0
  38. package/build/client/routing/loader.js +53 -7
  39. package/build/client/routing/mount.js +4 -3
  40. package/build/compiler/.tsbuildinfo +1 -1
  41. package/build/compiler/config.d.ts +18 -0
  42. package/build/compiler/config.js +8 -0
  43. package/build/compiler/docs.js +16 -16
  44. package/build/compiler/generate.js +3 -0
  45. package/build/compiler/index.d.ts +2 -2
  46. package/build/compiler/index.js +3 -1
  47. package/build/compiler/plugin.js +156 -0
  48. package/build/compiler/prerender.d.ts +1 -0
  49. package/build/compiler/prerender.js +2 -1
  50. package/build/compiler/seo.d.ts +2 -2
  51. package/build/compiler/seo.js +8 -6
  52. package/build/compiler/ssg.d.ts +5 -0
  53. package/build/compiler/ssg.js +121 -0
  54. package/build/io/.tsbuildinfo +1 -1
  55. package/build/logger/.tsbuildinfo +1 -1
  56. package/build/shared/.tsbuildinfo +1 -1
  57. package/eslint.config.js +48 -48
  58. package/examples/basic/client/404.tsx +11 -11
  59. package/examples/basic/client/components/.gitkeep +1 -1
  60. package/examples/basic/client/global-error.tsx +13 -13
  61. package/examples/basic/client/layout.tsx +25 -25
  62. package/examples/basic/client/public/images/.gitkeep +1 -1
  63. package/examples/basic/client/public/images/logo.svg +36 -36
  64. package/examples/basic/client/public/robots.txt +2 -2
  65. package/examples/basic/client/routes/docs/[...slug].tsx +12 -12
  66. package/examples/basic/client/routes/features/error/error.tsx +16 -16
  67. package/examples/basic/client/routes/features/template/b.tsx +14 -14
  68. package/examples/basic/client/routes/files/[[...slug]].tsx +21 -21
  69. package/examples/basic/client/routes/gallery/layout.tsx +13 -13
  70. package/examples/basic/client/routes/io.tsx +24 -24
  71. package/examples/basic/client/routes/loader-demo/loading.tsx +13 -13
  72. package/examples/basic/client/routes/search.tsx +61 -61
  73. package/examples/basic/client/toil.tsx +5 -5
  74. package/package.json +155 -147
  75. package/presets/eslint.js +88 -88
  76. package/presets/no-uint8array-tostring.js +200 -200
  77. package/presets/prettier.json +18 -18
  78. package/presets/tsconfig.json +37 -37
  79. package/src/backend/index.ts +160 -160
  80. package/src/cli/proc.ts +50 -50
  81. package/src/cli/updates.ts +69 -69
  82. package/src/cli/validate.ts +31 -31
  83. package/src/client/channel/channel.ts +146 -146
  84. package/src/client/components/Form.tsx +65 -65
  85. package/src/client/components/Script.tsx +113 -113
  86. package/src/client/components/Slot.tsx +21 -21
  87. package/src/client/dev/devtools.tsx +973 -0
  88. package/src/client/dev/error-overlay.tsx +30 -4
  89. package/src/client/head/head.ts +167 -167
  90. package/src/client/head/metadata.ts +19 -1
  91. package/src/client/index.ts +19 -9
  92. package/src/client/navigation/NavLink.tsx +86 -86
  93. package/src/client/navigation/navigation.ts +25 -5
  94. package/src/client/navigation/prefetch.ts +169 -130
  95. package/src/client/navigation/scroll.ts +53 -53
  96. package/src/client/routing/Router.tsx +8 -2
  97. package/src/client/routing/action.ts +122 -122
  98. package/src/client/routing/error-boundary.tsx +43 -43
  99. package/src/client/routing/hooks.ts +21 -6
  100. package/src/client/routing/loader.ts +325 -225
  101. package/src/client/routing/match.ts +47 -47
  102. package/src/client/routing/mount.tsx +54 -52
  103. package/src/client/routing/params-context.ts +10 -10
  104. package/src/client/routing/slot-context.ts +7 -7
  105. package/src/client/search/search.ts +189 -189
  106. package/src/client/search/use-page-search.ts +73 -73
  107. package/src/client/types.ts +73 -73
  108. package/src/compiler/config.ts +47 -1
  109. package/src/compiler/docs.ts +228 -228
  110. package/src/compiler/generate.ts +394 -391
  111. package/src/compiler/index.ts +64 -54
  112. package/src/compiler/pages.ts +70 -70
  113. package/src/compiler/plugin.ts +170 -2
  114. package/src/compiler/prerender.ts +5 -1
  115. package/src/compiler/seo.ts +23 -7
  116. package/src/compiler/ssg.ts +162 -0
  117. package/src/io/BinaryReader.ts +340 -340
  118. package/src/io/BinaryWriter.ts +385 -385
  119. package/src/io/FastMap.ts +127 -127
  120. package/src/io/index.ts +11 -11
  121. package/src/io/lengths.ts +14 -14
  122. package/src/io/types.ts +18 -18
  123. package/src/logger/index.ts +22 -22
  124. package/src/server/index.ts +10 -10
  125. package/src/server/main.ts +13 -13
  126. package/src/server/tsconfig.json +4 -4
  127. package/src/shared/index.ts +10 -10
  128. package/std/client/index.d.ts +15 -15
  129. package/std/client/package.json +3 -3
  130. package/test/assembly/example.spec.ts +7 -7
  131. package/test/channel.test.ts +21 -21
  132. package/test/dom/Link.test.tsx +47 -47
  133. package/test/dom/NavLink.test.tsx +37 -37
  134. package/test/dom/error-overlay.test.tsx +44 -44
  135. package/test/dom/loader.test.tsx +121 -121
  136. package/test/dom/navigation.test.ts +59 -59
  137. package/test/dom/revalidate.test.tsx +38 -38
  138. package/test/dom/route-head.test.tsx +78 -78
  139. package/test/dom/router-loading.test.tsx +44 -44
  140. package/test/dom/scroll.test.ts +56 -56
  141. package/test/dom/use-metadata.test.tsx +58 -0
  142. package/test/io.test.ts +93 -93
  143. package/test/navlink.test.ts +28 -28
  144. package/test/placeholder.test.ts +9 -9
  145. package/test/routes.test.ts +76 -76
  146. package/test/seo.test.ts +175 -164
  147. package/test/slot-layouts.test.ts +69 -69
  148. package/test/ssg.test.ts +36 -0
  149. package/test/update.test.ts +44 -44
  150. package/test/validate.test.ts +42 -42
  151. package/toil-routes.d.ts +7 -0
  152. package/toilconfig.json +30 -30
  153. package/tsconfig.backend.json +13 -13
  154. package/tsconfig.base.json +35 -35
  155. package/tsconfig.cli.json +13 -13
  156. package/tsconfig.client.json +14 -14
  157. package/tsconfig.compiler.json +13 -13
  158. package/tsconfig.io.json +12 -12
  159. package/tsconfig.json +22 -22
  160. package/tsconfig.logger.json +12 -12
  161. package/tsconfig.server.json +10 -10
  162. package/tsconfig.shared.json +12 -12
  163. package/vitest.config.ts +26 -26
  164. package/.idea/codeStyles/Project.xml +0 -54
  165. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  166. package/.idea/inspectionProfiles/Project_Default.xml +0 -6
  167. package/.idea/modules.xml +0 -8
  168. package/.idea/prettier.xml +0 -7
  169. package/.idea/toiljs.iml +0 -8
  170. package/.idea/vcs.xml +0 -6
  171. package/.toil/entry.tsx +0 -9
  172. package/.toil/index.html +0 -12
  173. package/.toil/routes.ts +0 -9
  174. package/build/cli/configure.d.ts +0 -16
  175. package/build/cli/configure.js +0 -272
  176. package/build/cli/create.d.ts +0 -16
  177. package/build/cli/create.js +0 -420
  178. package/build/cli/diagnostics.d.ts +0 -55
  179. package/build/cli/diagnostics.js +0 -333
  180. package/build/cli/doctor.d.ts +0 -6
  181. package/build/cli/doctor.js +0 -249
  182. package/build/cli/features.d.ts +0 -25
  183. package/build/cli/features.js +0 -107
  184. package/build/cli/index.d.ts +0 -2
  185. package/build/cli/proc.d.ts +0 -6
  186. package/build/cli/proc.js +0 -31
  187. package/build/cli/ui.d.ts +0 -9
  188. package/build/cli/ui.js +0 -75
  189. package/build/cli/update.d.ts +0 -7
  190. package/build/cli/update.js +0 -117
  191. package/build/cli/updates.d.ts +0 -10
  192. package/build/cli/updates.js +0 -45
  193. package/build/cli/validate.d.ts +0 -4
  194. package/build/cli/validate.js +0 -19
  195. package/build/client/Link.d.ts +0 -8
  196. package/build/client/Link.js +0 -44
  197. package/build/client/NavLink.d.ts +0 -14
  198. package/build/client/NavLink.js +0 -37
  199. package/build/client/Router.d.ts +0 -7
  200. package/build/client/Router.js +0 -55
  201. package/build/client/channel.d.ts +0 -23
  202. package/build/client/channel.js +0 -94
  203. package/build/client/error-boundary.d.ts +0 -16
  204. package/build/client/error-boundary.js +0 -19
  205. package/build/client/head.d.ts +0 -26
  206. package/build/client/head.js +0 -87
  207. package/build/client/hooks.d.ts +0 -17
  208. package/build/client/hooks.js +0 -48
  209. package/build/client/lazy.d.ts +0 -16
  210. package/build/client/lazy.js +0 -53
  211. package/build/client/match.d.ts +0 -2
  212. package/build/client/match.js +0 -32
  213. package/build/client/mount.d.ts +0 -2
  214. package/build/client/mount.js +0 -13
  215. package/build/client/navigation.d.ts +0 -13
  216. package/build/client/navigation.js +0 -97
  217. package/build/client/params-context.d.ts +0 -2
  218. package/build/client/params-context.js +0 -2
  219. package/build/client/prefetch.d.ts +0 -11
  220. package/build/client/prefetch.js +0 -100
  221. package/build/client/runtime.d.ts +0 -31
  222. package/build/client/runtime.js +0 -112
  223. package/build/client/scroll.d.ts +0 -8
  224. package/build/client/scroll.js +0 -36
  225. package/toil-env.d.ts +0 -16
package/README.md CHANGED
@@ -1,315 +1,339 @@
1
- <div align="center">
2
-
3
- <img src="https://raw.githubusercontent.com/dacely-cloud/toiljs/main/assets/logo.svg" alt="ToilJS" width="128" height="128" />
4
-
5
- # ToilJS
6
-
7
- ### Everything React forgot to ship.
8
-
9
- <sub>Fast by design. Architecture chosen for hyper scale: 50 Gbit/s on commodity hardware.</sub>
10
-
11
- <br/>
12
-
13
- [![npm](https://img.shields.io/npm/v/toiljs.svg?color=2563ff&label=npm&labelColor=0e1520)](https://www.npmjs.com/package/toiljs)
14
- [![types](https://img.shields.io/badge/types-included-2563ff.svg?labelColor=0e1520)](https://www.typescriptlang.org/)
15
- [![react](https://img.shields.io/badge/react-19-22e3ab.svg?labelColor=0e1520)](https://react.dev/)
16
- [![server](https://img.shields.io/badge/server-WebAssembly-7c3aed.svg?labelColor=0e1520)](#built-for-scale)
17
- [![license](https://img.shields.io/badge/license-Apache--2.0-8b9ab4.svg?labelColor=0e1520)](./LICENSE)
18
-
19
- <br/>
20
-
21
- <img src="https://img.shields.io/badge/React_19-20232a?style=for-the-badge&logo=react&logoColor=61dafb" alt="React 19" />
22
- <img src="https://img.shields.io/badge/TypeScript-3178c6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
23
- <img src="https://img.shields.io/badge/Vite-646cff?style=for-the-badge&logo=vite&logoColor=white" alt="Vite" />
24
- <img src="https://img.shields.io/badge/WebAssembly-654ff0?style=for-the-badge&logo=webassembly&logoColor=white" alt="WebAssembly" />
25
-
26
- </div>
27
-
28
- ---
29
-
30
- React gives you a renderer and leaves the rest to you: a router, a bundler, data fetching, SEO, an image pipeline, a server. ToilJS is all of it, already wired.
31
-
32
- ```bash
33
- npx toiljs create my-app
34
- cd my-app
35
- npm run dev
36
- ```
37
-
38
- Drop a `.tsx` file in `client/routes/` and it is a route: typed, code-split, prefetched, data loaded before render. The `server/` compiles to WebAssembly and self-hosts on uWebSockets. You configured nothing.
39
-
40
- ## Built for scale
41
-
42
- The backend is the point. `server/` is [ToilScript](https://www.npmjs.com/package/toilscript) compiled to a single WebAssembly module (Binaryen), and `toiljs start` self-hosts the app on [hyper-express](https://github.com/kartikk221/hyper-express), backed by [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js), the same core that serves millions of HTTP requests per second.
43
-
44
- <div align="center">
45
-
46
- ![throughput](https://img.shields.io/badge/throughput-50_Gbit%2Fs-2563ff?style=for-the-badge)
47
- ![requests](https://img.shields.io/badge/requests-millions%2Fsec-7c3aed?style=for-the-badge)
48
- ![compute](https://img.shields.io/badge/compute-native_WASM-22e3ab?style=for-the-badge)
49
- ![transport](https://img.shields.io/badge/transport-HTTP%2F3_+_WebTransport-654ff0?style=for-the-badge)
50
-
51
- </div>
52
-
53
- - **WebAssembly compute.** Your server logic runs as native-speed WASM, not interpreted JS.
54
- - **Binary on the wire.** The client and server share `BinaryWriter` / `BinaryReader` and `FastMap` / `FastSet`, so you move bytes, not JSON.
55
- - **HTTP/3 and WebTransport** over QUIC, low-latency streaming without the TCP head-of-line tax.
56
- - **Built for 50 Gbit/s on commodity hardware** and millions of requests per second, not toy demos.
57
-
58
- ```ts
59
- // server/index.ts
60
- export function add(a: i32, b: i32): i32 {
61
- return a + b;
62
- }
63
- ```
64
-
65
- ## On by default
66
-
67
- Every one of these works the moment you run `create`. No plugins to install, no config to write:
68
-
69
- - **Build-time SEO for an SPA**: prerendered `<head>`, `robots.txt`, `sitemap.xml`, `llms.txt`
70
- - **AI-crawler rules**: per-bot `robots.txt` plus `llms.txt`, one switch
71
- - **Image optimization on**: imports and plain `<img>` become resized webp
72
- - **Fonts preloaded** at build
73
- - **Typed routes**: `href` and `params` checked against your real files
74
- - **Loaders and mutations** with caching and revalidation, no data library, no `useEffect` fetching
75
- - **Parallel and intercepting routes** for modals and dashboards
76
- - **Instant navigation** and **animated view transitions**
77
- - **WebAssembly backend** on uWebSockets, with **binary IO** on both sides
78
-
79
- Anywhere else, that is a dozen packages and their config. Most teams wire three and ship the rest half-done.
80
-
81
- ## AI-ready
82
-
83
- ToilJS treats AI crawlers as first-class. Turn on SEO and the build emits an `llms.txt` describing your site for LLMs, and a `robots.txt` with explicit rules for the AI bots, allow or block GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, anthropic-ai, Google-Extended, PerplexityBot, CCBot, Applebot-Extended, Bytespider, Amazonbot, and Meta-ExternalAgent, with one switch.
84
-
85
- ```ts
86
- seo: { llms: { instructions: 'Docs live at /docs.' }, robots: { ai: 'disallow' } }
87
- ```
88
-
89
- That one line produces a real `robots.txt`:
90
-
91
- ```
92
- User-agent: *
93
- Allow: /
94
-
95
- User-agent: GPTBot
96
- Disallow: /
97
-
98
- User-agent: ClaudeBot
99
- Disallow: /
100
-
101
- User-agent: Google-Extended
102
- Disallow: /
103
-
104
- Sitemap: https://example.com/sitemap.xml
105
- ```
106
-
107
- The `toiljs create` wizard can also scaffold assistant files (CLAUDE.md, AGENTS.md, Cursor and Copilot configs) so your repo is ready for coding agents on day one.
108
-
109
- ## Everything, at a glance
110
-
111
- | | |
112
- | --- | --- |
113
- | **Routing** | File-based. Dynamic, catch-all, optional catch-all, route groups, nested layouts, templates, parallel slots, and intercepting routes. Every `href` and `params` is typed. |
114
- | **Data** | A `loader` resolves before render. `useAction` / `<Form>` write then revalidate. Per-route caching. No fetch waterfalls. |
115
- | **SEO** | Per-route metadata baked into static HTML, plus `robots.txt`, `sitemap.xml`, `llms.txt`, OpenGraph, Twitter, JSON-LD, canonical, theme-color, early hints. |
116
- | **Assets** | Imported images compressed to webp and resized. Fonts preloaded. React split for caching. Build logs what it saved. |
117
- | **Realtime** | Built-in WebSocket channels: `connectChannel` / `useChannel`. WebTransport over HTTP/3. |
118
- | **DX** | HMR, instant navigation, view transitions, typed routes, and a dev error overlay. |
119
- | **Server** | ToilScript compiled to WebAssembly, self-hosted on uWebSockets with HTTP/3. Binary IO built in. |
120
- | **Tooling** | Strict TypeScript, ESLint, and Prettier, configured and enforced out of the box. Tailwind v4 optional. |
121
-
122
- ## Routing
123
-
124
- The filesystem is the router.
125
-
126
- | File or folder | Route |
127
- | --- | --- |
128
- | `index.tsx` | `/` |
129
- | `about.tsx` | `/about` |
130
- | `blog/[id].tsx` | `/blog/:id` |
131
- | `docs/[...slug].tsx` | catch-all |
132
- | `docs/[[...slug]].tsx` | optional catch-all |
133
- | `(marketing)/about.tsx` | route group, adds no URL segment |
134
- | `layout.tsx` | wraps the segment, persists across navigation |
135
- | `template.tsx` | a layout that re-mounts on every navigation |
136
- | `loading.tsx` | Suspense fallback while the route and its data load |
137
- | `error.tsx` | error boundary for the segment |
138
- | `global-error.tsx` | catches errors in the root layout itself |
139
- | `404.tsx` | not-found page |
140
- | `@modal/...` | parallel slot, placed with `<Toil.Slot name="modal" />` |
141
- | `@modal/(.)photo/[id]` | intercepting route: modal on soft nav, full page on reload |
142
-
143
- Navigation comes with it:
144
-
145
- - **`<Toil.Link>`** and **`<Toil.NavLink>`** (active class + `aria-current`), with `href` checked against your real routes.
146
- - **`navigate` / `back` / `forward` / `refresh`**, plus **`useRouter`**, **`useNavigate`**, **`useLocation`**, **`usePathname`**, **`useParams`**, **`useSearchParams`**, **`useNavigationPending`**.
147
- - **Hover and viewport prefetching**, so chunks are warm before you click.
148
- - **Scroll restoration** on back/forward, scroll-to-`#hash`, and scroll-to-top on new routes.
149
- - **Instant navigation**: visited pages render synchronously, no flash.
150
- - **View transitions** (`client.viewTransitions: true`) for animated page changes, respecting `prefers-reduced-motion`.
151
-
152
- ## Data
153
-
154
- Read with a `loader`, write with an action. Both keep the UI in sync without manual refetching.
155
-
156
- ```tsx
157
- export const loader = async ({ params }: Toil.LoaderArgs) => fetchPost(params.id);
158
- export const revalidate: Toil.Revalidate = 10; // reuse for 10s, false = forever, omit = every nav
159
-
160
- function SaveButton({ title }: { title: string }) {
161
- const save = Toil.useAction((t: string) => api.save(t), { revalidate: true });
162
- return (
163
- <button disabled={save.pending} onClick={() => void save.run(title)}>
164
- {save.pending ? 'Saving' : 'Save'}
165
- </button>
166
- );
167
- }
168
- ```
169
-
170
- - **`loader`** resolves in parallel with the route chunk; the page suspends until ready (its `loading.tsx` shows).
171
- - **`useLoaderData(loader)`** is typed straight from the loader, no generics.
172
- - **`revalidate`** sets the cache policy per route; **`router.revalidate()`** / **`revalidate(href)`** bust it after a mutation.
173
- - **`useAction`** and **`<Toil.Form>`** track pending and error state and revalidate on success.
174
-
175
- ## Components
176
-
177
- Zero-import, on the `Toil` global:
178
-
179
- - **`Image`** drops in for `<img>`: reserves space (no layout shift), lazy-loads, async-decodes, `priority` for the LCP image, `fill` + `objectFit`, optional blur placeholder.
180
- - **`Script`** loads external or inline scripts with a `strategy` (`afterInteractive` / `lazyOnload` / `beforeInteractive`), deduplicated so a script never runs twice.
181
- - **`Form`** submits to an action without a reload, revalidates on success, exposes pending state, optionally resets fields.
182
- - **`Slot`** renders a parallel `@slot` route, the basis for modal overlays.
183
- - **`Head`** / **`useHead`** / **`useTitle`** set the title and `<meta>` / `<link>` tags imperatively and compose across the tree.
184
-
185
- ## Head and SEO
186
-
187
- A single-page app serves an empty shell. ToilJS pre-renders each route's `<head>` at build, so Google, Facebook, Discord, Slack, and the AI crawlers see real per-page tags without running your JavaScript.
188
-
189
- ```ts
190
- // toil.config.ts
191
- export default defineConfig({
192
- client: {
193
- seo: {
194
- url: 'https://example.com',
195
- title: 'My App',
196
- openGraph: { siteName: 'My App', type: 'website', image: '/og.png' },
197
- twitter: { card: 'summary_large_image' },
198
- jsonLd: { '@context': 'https://schema.org', '@type': 'WebSite' },
199
- themeColor: '#2563ff', // also the Discord / Slack embed accent
200
- preconnect: ['https://cdn.example.com'],
201
- robots: { ai: 'allow' },
202
- },
203
- },
204
- });
205
- ```
206
-
207
- - **Per-route `metadata`** (or `generateMetadata` derived from the loader's data) wins per page over layout defaults.
208
- - **Static prerender** writes a `<route>/index.html` for every static route with that route's head baked in.
209
- - **`robots.txt`**, **`sitemap.xml`**, and **`llms.txt`** generated together.
210
- - Full **OpenGraph** (image alt/width/height/type, locale), **Twitter card**, **`fb:app_id`**, **JSON-LD**, **canonical**, **theme-color**, and **`preconnect` / `dns-prefetch`** early hints.
211
- - Output is **XSS-hardened**: attribute values and inline JSON-LD are escaped so injected data can't break out.
212
-
213
- ## Build and assets
214
-
215
- ToilJS owns Vite and does the boring optimization for you. The build tells you what it did:
216
-
217
- ```
218
- $ npm run build
219
- ✓ optimized 3 images
220
- client/hero.png
221
- → images/hero.webp 148.0 kB → 19.3 kB -87%
222
- ✓ preloaded 2 fonts
223
- → fonts/inter-latin.woff2 24.10 kB
224
- ```
225
-
226
- - **Images** (`vite-imagetools` + `sharp`): every imported raster is compressed to webp, resize and reformat with `?w=400;800&format=webp&as=srcset`. The build logs the savings.
227
- - **Fonts**: bundled `@font-face` fonts get a `<link rel="preload">` so text paints sooner, also logged.
228
- - **Chunking**: React is split into its own long-lived chunk; assets land in tidy `images/`, `fonts/`, and `css/` folders.
229
- - **Node polyfills** (`Buffer`, `global`, `process`) for libraries that expect them.
230
- - **Styling**: plain CSS out of the box, with Sass, Less, Stylus, and Tailwind v4 a `toiljs configure` away.
231
-
232
- ## Realtime
233
-
234
- A typed WebSocket channel to the server, built in.
235
-
236
- ```tsx
237
- const messages = Toil.useChannel<Message>('/chat');
238
- ```
239
-
240
- `connectChannel` / `useChannel` / `resolveChannelUrl` handle connection, reconnection, and message decoding.
241
-
242
- ## Binary IO
243
-
244
- The same primitives on both sides of the wire, available as globals (and `toiljs/io`): `BinaryWriter`, `BinaryReader`, `FastMap`, `FastSet`. Move structured data without the JSON tax.
245
-
246
- ## Tooling is the standard
247
-
248
- ToilJS sets the toolchain so nobody argues about it. Strict TypeScript, ESLint, and Prettier come configured and enforced from the first commit. New apps are wired automatically, nothing to set up, nothing to copy, nothing to bikeshed. This is the standard, not a suggestion.
249
-
250
- ## CLI
251
-
252
- ```
253
- toiljs create [name] scaffold a new app (styling, AI files, package manager)
254
- toiljs dev dev server with HMR
255
- toiljs build production build
256
- toiljs start self-host the build (hyper-express / uWebSockets)
257
- toiljs configure toggle styling, image, font, and SEO features
258
- toiljs doctor diagnose project setup and dependencies (--json for CI)
259
- toiljs update check for and apply dependency updates (-y to apply all)
260
- ```
261
-
262
- ## Tech
263
-
264
- <div align="center">
265
-
266
- <img src="https://img.shields.io/badge/React_19-20232a?style=for-the-badge&logo=react&logoColor=61dafb" alt="React 19" />
267
- <img src="https://img.shields.io/badge/TypeScript-3178c6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
268
- <img src="https://img.shields.io/badge/Vite-646cff?style=for-the-badge&logo=vite&logoColor=white" alt="Vite" />
269
- <img src="https://img.shields.io/badge/WebAssembly-654ff0?style=for-the-badge&logo=webassembly&logoColor=white" alt="WebAssembly" />
270
- <img src="https://img.shields.io/badge/sharp-99cc00?style=for-the-badge&logo=sharp&logoColor=white" alt="sharp" />
271
- <img src="https://img.shields.io/badge/ESLint-4b32c3?style=for-the-badge&logo=eslint&logoColor=white" alt="ESLint" />
272
- <img src="https://img.shields.io/badge/Prettier-f7b93e?style=for-the-badge&logo=prettier&logoColor=black" alt="Prettier" />
273
- <img src="https://img.shields.io/badge/Tailwind_v4-06b6d4?style=for-the-badge&logo=tailwindcss&logoColor=white" alt="Tailwind v4" />
274
-
275
- </div>
276
-
277
- React 19, TypeScript, Vite, ToilScript (compiles to WebAssembly, on Binaryen), hyper-express + uWebSockets.js, vite-imagetools + sharp, ESLint (typescript-eslint, react-hooks, react-refresh, @eslint-react), Prettier, Tailwind v4 (optional).
278
-
279
- ## One file does a lot
280
-
281
- ```tsx
282
- // client/routes/posts/[id].tsx -> /posts/:id
283
- interface Post {
284
- title: string;
285
- }
286
-
287
- export const metadata: Toil.Metadata = { title: 'Post' }; // SEO, baked into the HTML at build
288
-
289
- export const loader = async ({ params }: Toil.LoaderArgs): Promise<Post> => {
290
- const res = await fetch(`/api/posts/${params.id}`); // runs before render, no useEffect
291
- return res.json();
292
- };
293
-
294
- export default function PostPage() {
295
- const post = Toil.useLoaderData(loader); // typed Post, no generics
296
- return (
297
- <article>
298
- <h1>{post.title}</h1>
299
- <Toil.Link href="/posts">All posts</Toil.Link> {/* href is type-checked */}
300
- </article>
301
- );
302
- }
303
- ```
304
-
305
- No imports. `Toil` is a fully-typed global, tree-shaken at build. The page renders with its data already loaded.
306
-
307
- ## Start
308
-
309
- ```bash
310
- npx toiljs create my-app
311
- ```
312
-
313
- Everything in this README is already on. You just build the app.
314
-
315
- <div align="center"><br/><sub>Apache-2.0</sub></div>
1
+ <div align="center">
2
+
3
+ <img src="https://raw.githubusercontent.com/dacely-cloud/toiljs/main/assets/logo.svg" alt="ToilJS" width="128" height="128" />
4
+
5
+ # ToilJS
6
+
7
+ ### Everything React forgot to ship.
8
+
9
+ <sub>Fast by design. Architecture chosen for hyper scale: 50 Gbit/s on commodity hardware.</sub>
10
+
11
+ <br/>
12
+
13
+ [![npm](https://img.shields.io/npm/v/toiljs.svg?color=2563ff&label=npm&labelColor=0e1520)](https://www.npmjs.com/package/toiljs)
14
+ [![types](https://img.shields.io/badge/types-included-2563ff.svg?labelColor=0e1520)](https://www.typescriptlang.org/)
15
+ [![react](https://img.shields.io/badge/react-19-22e3ab.svg?labelColor=0e1520)](https://react.dev/)
16
+ [![server](https://img.shields.io/badge/server-WebAssembly-7c3aed.svg?labelColor=0e1520)](#built-for-scale)
17
+ [![license](https://img.shields.io/badge/license-Apache--2.0-8b9ab4.svg?labelColor=0e1520)](./LICENSE)
18
+
19
+ <br/>
20
+
21
+ <img src="https://img.shields.io/badge/React_19-20232a?style=for-the-badge&logo=react&logoColor=61dafb" alt="React 19" />
22
+ <img src="https://img.shields.io/badge/TypeScript-3178c6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
23
+ <img src="https://img.shields.io/badge/Vite-646cff?style=for-the-badge&logo=vite&logoColor=white" alt="Vite" />
24
+ <img src="https://img.shields.io/badge/WebAssembly-654ff0?style=for-the-badge&logo=webassembly&logoColor=white" alt="WebAssembly" />
25
+
26
+ </div>
27
+
28
+ ---
29
+
30
+ React gives you a renderer and leaves the rest to you: a router, a bundler, data fetching, SEO, an image pipeline, a server. ToilJS is all of it, already wired.
31
+
32
+ ```bash
33
+ npx toiljs create my-app
34
+ cd my-app
35
+ npm run dev
36
+ ```
37
+
38
+ Drop a `.tsx` file in `client/routes/` and it is a route: typed, code-split, prefetched, data loaded before render. The `server/` compiles to WebAssembly and self-hosts on uWebSockets. You configured nothing.
39
+
40
+ ## Built for scale
41
+
42
+ The backend is the point. `server/` is [ToilScript](https://www.npmjs.com/package/toilscript) compiled to a single WebAssembly module (Binaryen), and `toiljs start` self-hosts the app on [hyper-express](https://github.com/kartikk221/hyper-express), backed by [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js), the same core that serves millions of HTTP requests per second.
43
+
44
+ <div align="center">
45
+
46
+ ![throughput](https://img.shields.io/badge/throughput-50_Gbit%2Fs-2563ff?style=for-the-badge)
47
+ ![requests](https://img.shields.io/badge/requests-millions%2Fsec-7c3aed?style=for-the-badge)
48
+ ![compute](https://img.shields.io/badge/compute-native_WASM-22e3ab?style=for-the-badge)
49
+ ![transport](https://img.shields.io/badge/transport-HTTP%2F3_+_WebTransport-654ff0?style=for-the-badge)
50
+
51
+ </div>
52
+
53
+ - **WebAssembly compute.** Your server logic runs as native-speed WASM, not interpreted JS.
54
+ - **Binary on the wire.** The client and server share `BinaryWriter` / `BinaryReader` and `FastMap` / `FastSet`, so you move bytes, not JSON.
55
+ - **HTTP/3 and WebTransport** over QUIC, low-latency streaming without the TCP head-of-line tax.
56
+ - **Built for 50 Gbit/s on commodity hardware** and millions of requests per second, not toy demos.
57
+
58
+ ```ts
59
+ // server/index.ts
60
+ export function add(a: i32, b: i32): i32 {
61
+ return a + b;
62
+ }
63
+ ```
64
+
65
+ ## On by default
66
+
67
+ Every one of these works the moment you run `create`. No plugins to install, no config to write:
68
+
69
+ - **Build-time SEO for an SPA**: prerendered `<head>`, `robots.txt`, `sitemap.xml`, `llms.txt`
70
+ - **AI-crawler rules**: per-bot `robots.txt` plus `llms.txt`, one switch
71
+ - **Image optimization on**: imports and plain `<img>` become resized webp
72
+ - **Fonts preloaded** at build
73
+ - **Typed routes**: `href` and `params` checked against your real files
74
+ - **Loaders and mutations** with caching and revalidation, no data library, no `useEffect` fetching
75
+ - **Parallel and intercepting routes** for modals and dashboards
76
+ - **Instant navigation** and **animated view transitions**
77
+ - **WebAssembly backend** on uWebSockets, with **binary IO** on both sides
78
+
79
+ Anywhere else, that is a dozen packages and their config. Most teams wire three and ship the rest half-done.
80
+
81
+ ## AI-ready
82
+
83
+ ToilJS treats AI crawlers as first-class. Turn on SEO and the build emits an `llms.txt` describing your site for LLMs, and a `robots.txt` with explicit rules for the AI bots, allow or block GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, anthropic-ai, Google-Extended, PerplexityBot, CCBot, Applebot-Extended, Bytespider, Amazonbot, and Meta-ExternalAgent, with one switch.
84
+
85
+ ```ts
86
+ seo: { llms: { instructions: 'Docs live at /docs.' }, robots: { ai: 'disallow' } }
87
+ ```
88
+
89
+ That one line produces a real `robots.txt`:
90
+
91
+ ```
92
+ User-agent: *
93
+ Allow: /
94
+
95
+ User-agent: GPTBot
96
+ Disallow: /
97
+
98
+ User-agent: ClaudeBot
99
+ Disallow: /
100
+
101
+ User-agent: Google-Extended
102
+ Disallow: /
103
+
104
+ Sitemap: https://example.com/sitemap.xml
105
+ ```
106
+
107
+ The `toiljs create` wizard can also scaffold assistant files (CLAUDE.md, AGENTS.md, Cursor and Copilot configs) so your repo is ready for coding agents on day one.
108
+
109
+ ## Everything, at a glance
110
+
111
+ | | |
112
+ | --- | --- |
113
+ | **Routing** | File-based. Dynamic, catch-all, optional catch-all, route groups, nested layouts, templates, parallel slots, and intercepting routes. Every `href` and `params` is typed. |
114
+ | **Data** | A `loader` resolves before render. `useAction` / `<Form>` write then revalidate. Per-route caching. No fetch waterfalls. |
115
+ | **SEO** | Per-route metadata baked into static HTML, plus `robots.txt`, `sitemap.xml`, `llms.txt`, OpenGraph, Twitter, JSON-LD, canonical, theme-color, early hints. |
116
+ | **Assets** | Imported images compressed to webp and resized. Fonts preloaded. React split for caching. Build logs what it saved. |
117
+ | **Realtime** | Built-in WebSocket channels: `connectChannel` / `useChannel`. WebTransport over HTTP/3. |
118
+ | **DX** | HMR, instant navigation, view transitions, typed routes, and a dev error overlay. |
119
+ | **Server** | ToilScript compiled to WebAssembly, self-hosted on uWebSockets with HTTP/3. Binary IO built in. |
120
+ | **Tooling** | Strict TypeScript, ESLint, and Prettier, configured and enforced out of the box. Tailwind v4 optional. |
121
+
122
+ ## Routing
123
+
124
+ The filesystem is the router.
125
+
126
+ | File or folder | Route |
127
+ | --- | --- |
128
+ | `index.tsx` | `/` |
129
+ | `about.tsx` | `/about` |
130
+ | `blog/[id].tsx` | `/blog/:id` |
131
+ | `docs/[...slug].tsx` | catch-all |
132
+ | `docs/[[...slug]].tsx` | optional catch-all |
133
+ | `(marketing)/about.tsx` | route group, adds no URL segment |
134
+ | `layout.tsx` | wraps the segment, persists across navigation |
135
+ | `template.tsx` | a layout that re-mounts on every navigation |
136
+ | `loading.tsx` | Suspense fallback while the route and its data load |
137
+ | `error.tsx` | error boundary for the segment |
138
+ | `global-error.tsx` | catches errors in the root layout itself |
139
+ | `404.tsx` | not-found page |
140
+ | `@modal/...` | parallel slot, placed with `<Toil.Slot name="modal" />` |
141
+ | `@modal/(.)photo/[id]` | intercepting route: modal on soft nav, full page on reload |
142
+
143
+ Navigation comes with it:
144
+
145
+ - **`<Toil.Link>`** and **`<Toil.NavLink>`** (active class + `aria-current`), with `href` checked against your real routes.
146
+ - **`navigate` / `back` / `forward` / `refresh`**, plus **`useRouter`**, **`useNavigate`**, **`useLocation`**, **`usePathname`**, **`useParams`**, **`useSearchParams`**, **`useNavigationPending`**.
147
+ - **Hover and viewport prefetching**, so chunks are warm before you click.
148
+ - **Scroll restoration** on back/forward, scroll-to-`#hash`, and scroll-to-top on new routes.
149
+ - **Instant navigation**: visited pages render synchronously, no flash.
150
+ - **View transitions** (`client.viewTransitions: true`) for animated page changes, respecting `prefers-reduced-motion`.
151
+
152
+ ## Data
153
+
154
+ Read with a `loader`, write with an action. Both keep the UI in sync without manual refetching.
155
+
156
+ ```tsx
157
+ export const loader = async ({ params }: Toil.LoaderArgs) => fetchPost(params.id);
158
+ export const revalidate: Toil.Revalidate = 10; // reuse for 10s, false = forever, omit = every nav
159
+
160
+ function SaveButton({ title }: { title: string }) {
161
+ const save = Toil.useAction((t: string) => api.save(t), { revalidate: true });
162
+ return (
163
+ <button disabled={save.pending} onClick={() => void save.run(title)}>
164
+ {save.pending ? 'Saving' : 'Save'}
165
+ </button>
166
+ );
167
+ }
168
+ ```
169
+
170
+ - **`loader`** resolves in parallel with the route chunk; the page suspends until ready (its `loading.tsx` shows).
171
+ - **`useLoaderData(loader)`** is typed straight from the loader, no generics.
172
+ - **`revalidate`** sets the cache policy per route; **`router.revalidate()`** / **`revalidate(href)`** bust it after a mutation.
173
+ - **`useAction`** and **`<Toil.Form>`** track pending and error state and revalidate on success.
174
+
175
+ ## Components
176
+
177
+ Zero-import, on the `Toil` global:
178
+
179
+ - **`Image`** drops in for `<img>`: reserves space (no layout shift), lazy-loads, async-decodes, `priority` for the LCP image, `fill` + `objectFit`, optional blur placeholder.
180
+ - **`Script`** loads external or inline scripts with a `strategy` (`afterInteractive` / `lazyOnload` / `beforeInteractive`), deduplicated so a script never runs twice.
181
+ - **`Form`** submits to an action without a reload, revalidates on success, exposes pending state, optionally resets fields.
182
+ - **`Slot`** renders a parallel `@slot` route, the basis for modal overlays.
183
+ - **`Head`** / **`useHead`** / **`useTitle`** set the title and `<meta>` / `<link>` tags imperatively and compose across the tree.
184
+
185
+ ## Head and SEO
186
+
187
+ A single-page app serves an empty shell. ToilJS pre-renders each route's `<head>` at build, so Google, Facebook, Discord, Slack, and the AI crawlers see real per-page tags without running your JavaScript.
188
+
189
+ ```ts
190
+ // toil.config.ts
191
+ export default defineConfig({
192
+ client: {
193
+ seo: {
194
+ url: 'https://example.com',
195
+ title: 'My App',
196
+ openGraph: { siteName: 'My App', type: 'website', image: '/og.png' },
197
+ twitter: { card: 'summary_large_image' },
198
+ jsonLd: { '@context': 'https://schema.org', '@type': 'WebSite' },
199
+ themeColor: '#2563ff', // also the Discord / Slack embed accent
200
+ preconnect: ['https://cdn.example.com'],
201
+ robots: { ai: 'allow' },
202
+ },
203
+ },
204
+ });
205
+ ```
206
+
207
+ - **Per-route `metadata`** (or `generateMetadata` derived from the loader's data) wins per page over layout defaults.
208
+ - **Static prerender** writes a `<route>/index.html` for every static route with that route's head baked in.
209
+ - **`robots.txt`**, **`sitemap.xml`**, and **`llms.txt`** generated together.
210
+ - Full **OpenGraph** (image alt/width/height/type, locale), **Twitter card**, **`fb:app_id`**, **JSON-LD**, **canonical**, **theme-color**, and **`preconnect` / `dns-prefetch`** early hints.
211
+ - Output is **XSS-hardened**: attribute values and inline JSON-LD are escaped so injected data can't break out.
212
+
213
+ ## Build and assets
214
+
215
+ ToilJS owns Vite and does the boring optimization for you. The build tells you what it did:
216
+
217
+ ```
218
+ $ npm run build
219
+ ✓ optimized 3 images
220
+ client/hero.png
221
+ → images/hero.webp 148.0 kB → 19.3 kB -87%
222
+ ✓ preloaded 2 fonts
223
+ → fonts/inter-latin.woff2 24.10 kB
224
+ ```
225
+
226
+ - **Images** (`vite-imagetools` + `sharp`): every imported raster is compressed to webp, resize and reformat with `?w=400;800&format=webp&as=srcset`. The build logs the savings.
227
+ - **Fonts**: bundled `@font-face` fonts get a `<link rel="preload">` so text paints sooner, also logged.
228
+ - **Chunking**: React is split into its own long-lived chunk; assets land in tidy `images/`, `fonts/`, and `css/` folders.
229
+ - **Node polyfills** (`Buffer`, `global`, `process`) for libraries that expect them.
230
+ - **Styling**: plain CSS out of the box, with Sass, Less, Stylus, and Tailwind v4 a `toiljs configure` away.
231
+
232
+ ## Realtime
233
+
234
+ A typed WebSocket channel to the server, built in.
235
+
236
+ ```tsx
237
+ const messages = Toil.useChannel<Message>('/chat');
238
+ ```
239
+
240
+ `connectChannel` / `useChannel` / `resolveChannelUrl` handle connection, reconnection, and message decoding.
241
+
242
+ ## Binary IO
243
+
244
+ The same primitives on both sides of the wire, available as globals (and `toiljs/io`): `BinaryWriter`, `BinaryReader`, `FastMap`, `FastSet`. Move structured data without the JSON tax.
245
+
246
+ ## Tooling is the standard
247
+
248
+ ToilJS sets the toolchain so nobody argues about it. Strict TypeScript, ESLint, and Prettier come configured and enforced from the first commit. New apps are wired automatically, nothing to set up, nothing to copy, nothing to bikeshed. This is the standard, not a suggestion.
249
+
250
+ ## CLI
251
+
252
+ ```
253
+ toiljs create [name] scaffold a new app (styling, AI files, package manager)
254
+ toiljs dev dev server with HMR
255
+ toiljs build production build
256
+ toiljs start self-host the build (hyper-express / uWebSockets)
257
+ toiljs configure toggle styling, image, font, and SEO features
258
+ toiljs doctor diagnose project setup and dependencies (--json for CI)
259
+ toiljs update check for and apply dependency updates (-y to apply all)
260
+ ```
261
+
262
+ ## Dev toolbar
263
+
264
+ `toiljs dev` injects a floating toolbar (bottom corner) that surfaces the framework's live state and is stripped from production builds entirely, no flag, no leftover bytes. It reads the matched route, params, and active slots; the loader cache (with revalidate and clear buttons); the live `<head>` with an OpenGraph preview and an SEO checklist; resolved config flags and versions; a captured error log; and live toggles for view and loader transitions. Click a route to navigate, or open its file in your editor.
265
+
266
+ It also ships an **AI tab**: hand off the current page's context to Claude or ChatGPT in one click, or wire a provider for inline answers. The key is read server-side and never reaches the browser.
267
+
268
+ ```ts
269
+ import { defineConfig, AiProvider } from 'toiljs/compiler';
270
+
271
+ export default defineConfig({
272
+ client: {
273
+ devtools: {
274
+ ai: {
275
+ provider: AiProvider.Anthropic, // or AiProvider.OpenAI, or a custom endpoint
276
+ model: 'claude-sonnet-4-6',
277
+ apiKeyEnv: 'ANTHROPIC_API_KEY', // read by the dev server only
278
+ },
279
+ },
280
+ },
281
+ });
282
+ ```
283
+
284
+ `devtools: false` turns it off; the hand-off links work with no config at all. Press `cmd`/`ctrl`+`K` for a command palette to jump to any route or run a dev action.
285
+
286
+ ## Tech
287
+
288
+ <div align="center">
289
+
290
+ <img src="https://img.shields.io/badge/React_19-20232a?style=for-the-badge&logo=react&logoColor=61dafb" alt="React 19" />
291
+ <img src="https://img.shields.io/badge/TypeScript-3178c6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
292
+ <img src="https://img.shields.io/badge/Vite-646cff?style=for-the-badge&logo=vite&logoColor=white" alt="Vite" />
293
+ <img src="https://img.shields.io/badge/WebAssembly-654ff0?style=for-the-badge&logo=webassembly&logoColor=white" alt="WebAssembly" />
294
+ <img src="https://img.shields.io/badge/sharp-99cc00?style=for-the-badge&logo=sharp&logoColor=white" alt="sharp" />
295
+ <img src="https://img.shields.io/badge/ESLint-4b32c3?style=for-the-badge&logo=eslint&logoColor=white" alt="ESLint" />
296
+ <img src="https://img.shields.io/badge/Prettier-f7b93e?style=for-the-badge&logo=prettier&logoColor=black" alt="Prettier" />
297
+ <img src="https://img.shields.io/badge/Tailwind_v4-06b6d4?style=for-the-badge&logo=tailwindcss&logoColor=white" alt="Tailwind v4" />
298
+
299
+ </div>
300
+
301
+ React 19, TypeScript, Vite, ToilScript (compiles to WebAssembly, on Binaryen), hyper-express + uWebSockets.js, vite-imagetools + sharp, ESLint (typescript-eslint, react-hooks, react-refresh, @eslint-react), Prettier, Tailwind v4 (optional).
302
+
303
+ ## One file does a lot
304
+
305
+ ```tsx
306
+ // client/routes/posts/[id].tsx -> /posts/:id
307
+ interface Post {
308
+ title: string;
309
+ }
310
+
311
+ export const metadata: Toil.Metadata = { title: 'Post' }; // SEO, baked into the HTML at build
312
+
313
+ export const loader = async ({ params }: Toil.LoaderArgs): Promise<Post> => {
314
+ const res = await fetch(`/api/posts/${params.id}`); // runs before render, no useEffect
315
+ return res.json();
316
+ };
317
+
318
+ export default function PostPage() {
319
+ const post = Toil.useLoaderData(loader); // typed Post, no generics
320
+ return (
321
+ <article>
322
+ <h1>{post.title}</h1>
323
+ <Toil.Link href="/posts">All posts</Toil.Link> {/* href is type-checked */}
324
+ </article>
325
+ );
326
+ }
327
+ ```
328
+
329
+ No imports. `Toil` is a fully-typed global, tree-shaken at build. The page renders with its data already loaded.
330
+
331
+ ## Start
332
+
333
+ ```bash
334
+ npx toiljs create my-app
335
+ ```
336
+
337
+ Everything in this README is already on. You just build the app.
338
+
339
+ <div align="center"><br/><sub>Apache-2.0</sub></div>