toiljs 0.0.15 → 0.0.19

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 (273) 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 +116 -5
  14. package/LICENSE +187 -187
  15. package/README.md +524 -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/backend/index.d.ts +1 -0
  21. package/build/backend/index.js +20 -1
  22. package/build/cli/.tsbuildinfo +1 -1
  23. package/build/cli/index.js +1320 -696
  24. package/build/client/.tsbuildinfo +1 -1
  25. package/build/client/dev/devtools.d.ts +6 -0
  26. package/build/client/dev/devtools.js +479 -0
  27. package/build/client/dev/error-overlay.d.ts +9 -0
  28. package/build/client/dev/error-overlay.js +19 -4
  29. package/build/client/errors.d.ts +1 -0
  30. package/build/client/errors.js +3 -0
  31. package/build/client/index.d.ts +2 -0
  32. package/build/client/index.js +2 -0
  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 +23 -0
  38. package/build/client/routing/loader.js +53 -7
  39. package/build/client/routing/mount.js +4 -3
  40. package/build/client/rpc.d.ts +1 -0
  41. package/build/client/rpc.js +37 -0
  42. package/build/compiler/.tsbuildinfo +1 -1
  43. package/build/compiler/config.d.ts +16 -0
  44. package/build/compiler/config.js +9 -0
  45. package/build/compiler/docs.js +78 -21
  46. package/build/compiler/generate.js +5 -4
  47. package/build/compiler/index.d.ts +3 -2
  48. package/build/compiler/index.js +2 -2
  49. package/build/compiler/plugin.js +228 -0
  50. package/build/compiler/prerender.d.ts +1 -0
  51. package/build/compiler/prerender.js +1 -1
  52. package/build/compiler/seo.d.ts +1 -1
  53. package/build/compiler/seo.js +20 -5
  54. package/build/compiler/ssg.js +39 -2
  55. package/build/compiler/vite.js +25 -0
  56. package/build/io/.tsbuildinfo +1 -1
  57. package/build/io/codec.d.ts +54 -0
  58. package/build/io/codec.js +143 -0
  59. package/build/io/index.d.ts +1 -2
  60. package/build/io/index.js +1 -2
  61. package/build/logger/.tsbuildinfo +1 -1
  62. package/build/shared/.tsbuildinfo +1 -1
  63. package/eslint.config.js +48 -48
  64. package/examples/basic/client/404.tsx +11 -11
  65. package/examples/basic/client/components/.gitkeep +1 -1
  66. package/examples/basic/client/global-error.tsx +13 -13
  67. package/examples/basic/client/layout.tsx +25 -25
  68. package/examples/basic/client/public/images/.gitkeep +1 -1
  69. package/examples/basic/client/public/images/logo.svg +36 -36
  70. package/examples/basic/client/public/robots.txt +2 -2
  71. package/examples/basic/client/routes/docs/[...slug].tsx +12 -12
  72. package/examples/basic/client/routes/features/error/error.tsx +16 -16
  73. package/examples/basic/client/routes/features/index.tsx +1 -1
  74. package/examples/basic/client/routes/features/template/b.tsx +14 -14
  75. package/examples/basic/client/routes/files/[[...slug]].tsx +21 -21
  76. package/examples/basic/client/routes/gallery/layout.tsx +13 -13
  77. package/examples/basic/client/routes/io.tsx +23 -24
  78. package/examples/basic/client/routes/loader-demo/loading.tsx +13 -13
  79. package/examples/basic/client/routes/rest.tsx +74 -0
  80. package/examples/basic/client/routes/rpc.tsx +43 -0
  81. package/examples/basic/client/routes/search.tsx +61 -61
  82. package/examples/basic/client/toil.tsx +5 -5
  83. package/package.json +167 -148
  84. package/presets/eslint.js +88 -88
  85. package/presets/no-uint8array-tostring.js +200 -200
  86. package/presets/prettier-plugin.js +51 -0
  87. package/presets/prettier.json +19 -18
  88. package/presets/tsconfig.json +37 -37
  89. package/server/runtime/README.md +97 -0
  90. package/server/runtime/abort/abort.ts +27 -0
  91. package/server/runtime/env/Server.ts +61 -0
  92. package/server/runtime/envelope.ts +191 -0
  93. package/server/runtime/exports/index.ts +52 -0
  94. package/server/runtime/handlers/ToilHandler.ts +34 -0
  95. package/server/runtime/index.ts +26 -0
  96. package/server/runtime/lang/Potential.ts +5 -0
  97. package/server/runtime/memory.ts +81 -0
  98. package/server/runtime/request.ts +55 -0
  99. package/server/runtime/response.ts +86 -0
  100. package/server/runtime/rest/Rest.ts +39 -0
  101. package/server/runtime/rest/RestHandler.ts +20 -0
  102. package/server/runtime/rest/RouteContext.ts +82 -0
  103. package/server/runtime/rest/match.ts +48 -0
  104. package/server/runtime/tsconfig.json +7 -0
  105. package/src/backend/index.ts +202 -160
  106. package/src/cli/create.ts +15 -5
  107. package/src/cli/diagnostics.ts +81 -0
  108. package/src/cli/doctor.ts +384 -7
  109. package/src/cli/index.ts +11 -2
  110. package/src/cli/proc.ts +50 -50
  111. package/src/cli/updates.ts +69 -69
  112. package/src/cli/validate.ts +31 -31
  113. package/src/client/channel/channel.ts +146 -146
  114. package/src/client/components/Form.tsx +65 -65
  115. package/src/client/components/Script.tsx +113 -113
  116. package/src/client/components/Slot.tsx +21 -21
  117. package/src/client/dev/devtools.tsx +1018 -0
  118. package/src/client/dev/error-overlay.tsx +30 -4
  119. package/src/client/errors.ts +11 -0
  120. package/src/client/head/head.ts +167 -167
  121. package/src/client/head/metadata.ts +112 -112
  122. package/src/client/index.ts +91 -89
  123. package/src/client/navigation/NavLink.tsx +86 -86
  124. package/src/client/navigation/navigation.ts +235 -235
  125. package/src/client/navigation/prefetch.ts +169 -130
  126. package/src/client/navigation/scroll.ts +53 -53
  127. package/src/client/routing/Router.tsx +8 -2
  128. package/src/client/routing/action.ts +122 -122
  129. package/src/client/routing/error-boundary.tsx +43 -43
  130. package/src/client/routing/hooks.ts +21 -6
  131. package/src/client/routing/loader.ts +325 -235
  132. package/src/client/routing/match.ts +47 -47
  133. package/src/client/routing/mount.tsx +54 -52
  134. package/src/client/routing/params-context.ts +10 -10
  135. package/src/client/routing/slot-context.ts +7 -7
  136. package/src/client/rpc.ts +64 -0
  137. package/src/client/search/search.ts +189 -189
  138. package/src/client/search/use-page-search.ts +73 -73
  139. package/src/client/types.ts +73 -73
  140. package/src/compiler/config.ts +221 -182
  141. package/src/compiler/docs.ts +285 -228
  142. package/src/compiler/generate.ts +395 -394
  143. package/src/compiler/index.ts +66 -57
  144. package/src/compiler/pages.ts +70 -70
  145. package/src/compiler/plugin.ts +258 -2
  146. package/src/compiler/prerender.ts +156 -156
  147. package/src/compiler/seo.ts +417 -390
  148. package/src/compiler/ssg.ts +171 -126
  149. package/src/compiler/vite.ts +34 -0
  150. package/src/io/FastMap.ts +151 -127
  151. package/src/io/FastSet.ts +15 -1
  152. package/src/io/codec.ts +217 -0
  153. package/src/io/index.ts +10 -11
  154. package/src/io/lengths.ts +14 -14
  155. package/src/io/types.ts +19 -18
  156. package/src/logger/index.ts +22 -22
  157. package/src/shared/index.ts +10 -10
  158. package/std/client/index.d.ts +15 -15
  159. package/std/client/package.json +3 -3
  160. package/test/assembly/example.spec.ts +17 -7
  161. package/test/channel.test.ts +21 -21
  162. package/test/doctor.test.ts +65 -0
  163. package/test/dom/Link.test.tsx +47 -47
  164. package/test/dom/NavLink.test.tsx +37 -37
  165. package/test/dom/error-overlay.test.tsx +44 -44
  166. package/test/dom/loader.test.tsx +121 -121
  167. package/test/dom/navigation.test.ts +59 -59
  168. package/test/dom/revalidate.test.tsx +38 -38
  169. package/test/dom/route-head.test.tsx +78 -78
  170. package/test/dom/router-loading.test.tsx +44 -44
  171. package/test/dom/scroll.test.ts +56 -56
  172. package/test/dom/use-metadata.test.tsx +58 -58
  173. package/test/errors.test.ts +21 -0
  174. package/test/io.test.ts +117 -93
  175. package/test/navlink.test.ts +28 -28
  176. package/test/placeholder.test.ts +9 -9
  177. package/test/prettier-plugin.test.ts +46 -0
  178. package/test/routes.test.ts +76 -76
  179. package/test/rpc.test.ts +50 -0
  180. package/test/seo.test.ts +175 -164
  181. package/test/slot-layouts.test.ts +69 -69
  182. package/test/ssg.test.ts +36 -36
  183. package/test/update.test.ts +44 -44
  184. package/test/validate.test.ts +42 -42
  185. package/tests/data-parity/generated-parity.ts +99 -0
  186. package/tests/data-parity/parity.ts +80 -0
  187. package/tests/data-parity/spec.ts +46 -0
  188. package/toil-routes.d.ts +7 -0
  189. package/tsconfig.backend.json +13 -13
  190. package/tsconfig.base.json +35 -35
  191. package/tsconfig.cli.json +13 -13
  192. package/tsconfig.client.json +14 -14
  193. package/tsconfig.compiler.json +13 -13
  194. package/tsconfig.io.json +12 -12
  195. package/tsconfig.json +22 -22
  196. package/tsconfig.logger.json +12 -12
  197. package/tsconfig.server.json +10 -10
  198. package/tsconfig.shared.json +12 -12
  199. package/vitest.config.ts +26 -26
  200. package/.idea/codeStyles/Project.xml +0 -54
  201. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  202. package/.idea/inspectionProfiles/Project_Default.xml +0 -6
  203. package/.idea/modules.xml +0 -8
  204. package/.idea/prettier.xml +0 -7
  205. package/.idea/toiljs.iml +0 -8
  206. package/.idea/vcs.xml +0 -6
  207. package/.toil/entry.tsx +0 -9
  208. package/.toil/index.html +0 -12
  209. package/.toil/routes.ts +0 -9
  210. package/build/cli/configure.d.ts +0 -16
  211. package/build/cli/configure.js +0 -272
  212. package/build/cli/create.d.ts +0 -16
  213. package/build/cli/create.js +0 -420
  214. package/build/cli/diagnostics.d.ts +0 -55
  215. package/build/cli/diagnostics.js +0 -333
  216. package/build/cli/doctor.d.ts +0 -6
  217. package/build/cli/doctor.js +0 -249
  218. package/build/cli/features.d.ts +0 -25
  219. package/build/cli/features.js +0 -107
  220. package/build/cli/index.d.ts +0 -2
  221. package/build/cli/proc.d.ts +0 -6
  222. package/build/cli/proc.js +0 -31
  223. package/build/cli/ui.d.ts +0 -9
  224. package/build/cli/ui.js +0 -75
  225. package/build/cli/update.d.ts +0 -7
  226. package/build/cli/update.js +0 -117
  227. package/build/cli/updates.d.ts +0 -10
  228. package/build/cli/updates.js +0 -45
  229. package/build/cli/validate.d.ts +0 -4
  230. package/build/cli/validate.js +0 -19
  231. package/build/client/Link.d.ts +0 -8
  232. package/build/client/Link.js +0 -44
  233. package/build/client/NavLink.d.ts +0 -14
  234. package/build/client/NavLink.js +0 -37
  235. package/build/client/Router.d.ts +0 -7
  236. package/build/client/Router.js +0 -55
  237. package/build/client/channel.d.ts +0 -23
  238. package/build/client/channel.js +0 -94
  239. package/build/client/error-boundary.d.ts +0 -16
  240. package/build/client/error-boundary.js +0 -19
  241. package/build/client/head.d.ts +0 -26
  242. package/build/client/head.js +0 -87
  243. package/build/client/hooks.d.ts +0 -17
  244. package/build/client/hooks.js +0 -48
  245. package/build/client/lazy.d.ts +0 -16
  246. package/build/client/lazy.js +0 -53
  247. package/build/client/match.d.ts +0 -2
  248. package/build/client/match.js +0 -32
  249. package/build/client/mount.d.ts +0 -2
  250. package/build/client/mount.js +0 -13
  251. package/build/client/navigation.d.ts +0 -13
  252. package/build/client/navigation.js +0 -97
  253. package/build/client/params-context.d.ts +0 -2
  254. package/build/client/params-context.js +0 -2
  255. package/build/client/prefetch.d.ts +0 -11
  256. package/build/client/prefetch.js +0 -100
  257. package/build/client/runtime.d.ts +0 -31
  258. package/build/client/runtime.js +0 -112
  259. package/build/client/scroll.d.ts +0 -8
  260. package/build/client/scroll.js +0 -36
  261. package/build/io/BinaryReader.d.ts +0 -44
  262. package/build/io/BinaryReader.js +0 -244
  263. package/build/io/BinaryWriter.d.ts +0 -44
  264. package/build/io/BinaryWriter.js +0 -297
  265. package/build/server/release.wasm +0 -0
  266. package/build/server/release.wat +0 -9
  267. package/src/io/BinaryReader.ts +0 -340
  268. package/src/io/BinaryWriter.ts +0 -385
  269. package/src/server/index.ts +0 -10
  270. package/src/server/main.ts +0 -13
  271. package/src/server/tsconfig.json +0 -4
  272. package/toil-env.d.ts +0 -16
  273. package/toilconfig.json +0 -30
package/README.md CHANGED
@@ -1,315 +1,524 @@
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
+ ### The fullstack React framework, built for hyperscale.
8
+
9
+ #### The first universal client and server framework.
10
+
11
+ <sub>Nothing to configure: routing, data, SEO, top-tier tooling, and full AI support, all built in.</sub>
12
+
13
+ <br/>
14
+
15
+ **⚡ First-class WebTransport over HTTP/3**, with automatic WebSocket fallback.
16
+
17
+ <br/>
18
+
19
+ [![npm](https://img.shields.io/npm/v/toiljs.svg?color=2563ff&label=npm&labelColor=0e1520)](https://www.npmjs.com/package/toiljs)
20
+ [![types](https://img.shields.io/badge/types-included-2563ff.svg?labelColor=0e1520)](https://www.typescriptlang.org/)
21
+ [![react](https://img.shields.io/badge/react-19-22e3ab.svg?labelColor=0e1520)](https://react.dev/)
22
+ [![server](https://img.shields.io/badge/server-WebAssembly-7c3aed.svg?labelColor=0e1520)](#the-server-toilscript--webassembly)
23
+ [![webtransport](https://img.shields.io/badge/WebTransport-coming-654ff0.svg?labelColor=0e1520)](#realtime)
24
+ [![license](https://img.shields.io/badge/license-Apache--2.0-8b9ab4.svg?labelColor=0e1520)](./LICENSE)
25
+
26
+ <br/>
27
+
28
+ <img src="https://img.shields.io/badge/React_19-20232a?style=for-the-badge&logo=react&logoColor=61dafb" alt="React 19" />
29
+ <img src="https://img.shields.io/badge/TypeScript-3178c6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
30
+ <img src="https://img.shields.io/badge/Vite-646cff?style=for-the-badge&logo=vite&logoColor=white" alt="Vite" />
31
+ <img src="https://img.shields.io/badge/WebAssembly-654ff0?style=for-the-badge&logo=webassembly&logoColor=white" alt="WebAssembly" />
32
+ <img src="https://img.shields.io/badge/ToilScript-cb9820?style=for-the-badge&logoColor=white" alt="ToilScript" />
33
+
34
+ </div>
35
+
36
+ ---
37
+
38
+ **ToilJS is a fullstack React framework built for hyperscale.** That is the whole point. Most React frameworks are built for convenience and start to buckle the moment real traffic arrives. Toil is built the other way around: for scale that serves the millionth request as easily as the first. Your client ships as a static bundle to the edge, and your server compiles to a single portable module designed to run at line rate, so the app you write on your laptop is shaped from day one to take serious load instead of folding under it.
39
+
40
+ And it is the entire stack, already wired and configured: routing, data, caching, SEO, site search, an image and font pipeline, realtime, a dev toolbar with AI, and a strict toolchain. One command scaffolds it, then you build your app, not your stack. Nothing to assemble, nothing to glue, nothing to configure.
41
+
42
+ ```bash
43
+ npx toiljs create my-app
44
+ cd my-app
45
+ npm run dev
46
+ ```
47
+
48
+ Drop a `.tsx` file in `client/routes/` and it is a route: typed, code-split, prefetched, with its data loaded before render. Your `server/` is written in **ToilScript** (TypeScript syntax) and compiles to a single WebAssembly module. You configured nothing.
49
+
50
+ <div align="center">
51
+
52
+ ```
53
+ your app
54
+ ┌──────────────────────┬──────────────────────┐
55
+ │ client/ │ server/ │
56
+ │ React + TSX │ ToilScript (.ts) │
57
+ │ static SPA bundle │ ──▶ WebAssembly │
58
+ └──────────┬───────────┴───────────┬───────────┘
59
+ │ │
60
+ CDN / static host WASM edge runtime
61
+ (typed RPC between them)
62
+ ```
63
+
64
+ </div>
65
+
66
+ The client is fully static (host it anywhere). The server is a portable compiled module. The two are separated by design and joined by a typed contract, so the frontend can ship to a CDN while the backend runs wherever it is deployed.
67
+
68
+ ## Built for scale
69
+
70
+ Toil's architecture is the scaling story. There is no monolith to keep warm and nothing re-rendering your pages on every request.
71
+
72
+ - **The client is static.** `build` emits a plain SPA bundle with each route's HTML already prerendered. It has no runtime server dependency, so it ships to any CDN or edge network and is cached and served close to every user.
73
+ - **The server is one portable module.** Your ToilScript backend compiles to a single self-contained module: no Node runtime to boot, no framework cold start, no per-request bootstrap. It is small, it starts instantly, and it runs the same everywhere.
74
+ - **Client and server are decoupled.** They are separate artifacts joined only by a typed contract, so the frontend scales as static files while the backend scales as stateless compute. Neither one bottlenecks the other.
75
+ - **Shaped for the edge.** That compiled module is exactly the unit a hyperscale edge runtime is built to serve: isolated per tenant, replicated across regions, run at line rate.
76
+
77
+ That is the platform Toil is being built toward. The architecture above ships today; this is the stack that runs it at planetary scale:
78
+
79
+ - **First-class WebTransport** *(coming)*: realtime over HTTP/3, multiplexed bidirectional streams and datagrams over QUIC with no head-of-line blocking. Toil is built to be the **first framework to ship first-class WebTransport**, and it falls back to WebSocket automatically, so the same channel API simply runs on the fastest transport available. WebSocket channels work today.
80
+ - **A purpose-built edge runtime** *(coming)*: your compiled server runs as an isolated tenant at line rate, replicated across regions, instead of on a general-purpose Node process.
81
+ - **ToilDB, edge-replicated typed data** *(coming)*: typed collections where the method name tells you the cost, local-fast reads, CRDT writes that merge everywhere, owner-routed writes, and rare global claims that are explicitly slow.
82
+ - **Post-quantum-ready transport** *(coming)*: forward-looking encryption as the QUIC layer lands.
83
+ - **Dacely Cloud** *(coming)*: managed hosting for the whole stack, push the app and the client goes to the edge while the server runs on the runtime.
84
+
85
+ The same app runs on your laptop and is shaped, from the first commit, to fan out across the edge without a rewrite. Full architecture and status in [The road to hyperscale](#the-road-to-hyperscale).
86
+
87
+ ## Everything, at a glance
88
+
89
+ This is the full surface area. Every row works the moment `create` finishes, no plugins to install, no config to write.
90
+
91
+ | | |
92
+ | --- | --- |
93
+ | **Zero config** | One command scaffolds a working app. You never write an `index.html`, a `main.tsx`, a router, or a Vite / build config. The framework generates and owns all of it. |
94
+ | **Routing** | File-based. Dynamic, catch-all, optional catch-all, route groups, nested layouts, templates, loading and error boundaries, parallel slots, and intercepting routes. Every `href` and `params` is typed against your real files. |
95
+ | **Navigation** | `Link` / `NavLink`, programmatic `navigate` / `back` / `forward` / `refresh`, hover and viewport prefetch, scroll restoration, instant back/forward, and animated view transitions. |
96
+ | **Data** | A `loader` resolves before render. `useAction` / `<Form>` write then revalidate. An LRU loader cache with per-route `revalidate`. No fetch waterfalls, no `useEffect` data fetching. |
97
+ | **Rendering + SEO** | Per-route `<head>` baked into static HTML at build, plus `sitemap.xml`, `robots.txt`, `llms.txt`, OpenGraph, Twitter, JSON-LD, canonical, theme-color, early hints. SSG via `generateStaticParams`. |
98
+ | **Search** | Built-in site search over a compiler-baked metadata index, ranked, with a `usePageSearch` hook. Plus `llms.txt` so AI crawlers can read your site. |
99
+ | **Assets** | Imported images compressed to webp and resized via Vite + sharp. Fonts preloaded. React split into its own long-lived chunk. The build logs what it saved. |
100
+ | **Components** | Drop-in `Image` (no layout shift, lazy, blur), `Script` (load strategies, dedupe), `Form`, `Slot`, `Head`, all on a typed `Toil` global, zero imports. |
101
+ | **Realtime** | Typed channels: `connectChannel` / `useChannel`, reconnect built in, text or binary frames. WebSocket today, with first-class WebTransport over HTTP/3 coming (automatic WebSocket fallback). |
102
+ | **Server** | A typed ToilScript server compiled to a portable, native-speed module. `Request` / `Response` REST handlers, binary IO on both sides, and a typed RPC surface generated from your server. |
103
+ | **Agentic DX** | A dev toolbar with a live AI tab (hand off page context to Claude or ChatGPT), a Cmd+K palette, and scaffolded agent files (CLAUDE.md, AGENTS.md, Cursor, Copilot). |
104
+ | **Toolkit** | Strict TypeScript, ESLint, and Prettier shipped as presets, plus optional git init. Tailwind v4, Sass, Less, and Stylus a flag away. |
105
+ | **CLI** | `create`, `dev`, `build`, `start`, `configure`, `doctor` (with `--json` for CI), and `update`. |
106
+
107
+ ## Routing
108
+
109
+ The filesystem is the router. Every convention below is implemented.
110
+
111
+ | File or folder | Route |
112
+ | --- | --- |
113
+ | `index.tsx` | `/` |
114
+ | `about.tsx` | `/about` |
115
+ | `blog/[id].tsx` | `/blog/:id` |
116
+ | `docs/[...slug].tsx` | catch-all |
117
+ | `docs/[[...slug]].tsx` | optional catch-all |
118
+ | `(marketing)/about.tsx` | route group, adds no URL segment |
119
+ | `layout.tsx` | wraps the segment, persists across navigation |
120
+ | `template.tsx` | a layout that re-mounts on every navigation |
121
+ | `loading.tsx` | Suspense fallback while the route and its data load |
122
+ | `error.tsx` | error boundary for the segment |
123
+ | `global-error.tsx` | catches errors in the root layout itself |
124
+ | `404.tsx` | not-found page |
125
+ | `@modal/...` | parallel slot, placed with `<Toil.Slot name="modal" />` |
126
+ | `@modal/(.)photo/[id]` | intercepting route: modal on soft nav, full page on reload |
127
+
128
+ Navigation comes with it:
129
+
130
+ - **`<Toil.Link>`** and **`<Toil.NavLink>`** (active class + `aria-current`), with `href` checked against your real routes.
131
+ - **`navigate` / `back` / `forward` / `refresh`**, plus **`useRouter`**, **`useNavigate`**, **`useLocation`**, **`usePathname`**, **`useParams`**, **`useSearchParams`**, **`useNavigationPending`**.
132
+ - **Hover and viewport prefetching**, so chunks (and data) are warm before you click. Respects `saveData` and `data-no-prefetch`.
133
+ - **Scroll restoration** on back/forward, scroll-to-`#hash`, and scroll-to-top on new routes.
134
+ - **Instant navigation**: visited pages render synchronously, no flash.
135
+ - **View transitions** (`client.viewTransitions: true`) for animated page changes, respecting `prefers-reduced-motion`.
136
+
137
+ ## Data and caching
138
+
139
+ Read with a `loader`, write with an action. Both keep the UI in sync without manual refetching, and the client caches loader results so repeat navigations are instant.
140
+
141
+ ```tsx
142
+ export const loader = async ({ params }: Toil.LoaderArgs) => fetchPost(params.id);
143
+ export const revalidate: Toil.Revalidate = 10; // reuse for 10s, false = forever, omit = every nav
144
+
145
+ function SaveButton({ title }: { title: string }) {
146
+ const save = Toil.useAction((t: string) => api.save(t), { revalidate: true });
147
+ return (
148
+ <button disabled={save.pending} onClick={() => void save.run(title)}>
149
+ {save.pending ? 'Saving' : 'Save'}
150
+ </button>
151
+ );
152
+ }
153
+ ```
154
+
155
+ - **`loader`** resolves in parallel with the route chunk; the page suspends until ready (its `loading.tsx` shows).
156
+ - **`useLoaderData(loader)`** is typed straight from the loader, no generics.
157
+ - **Client cache**: an LRU of loader results keyed by path + search, with `revalidate` set per route (seconds, `false` for forever, or per-navigation). Prefetched entries are reused on commit.
158
+ - **`useAction`** and **`<Toil.Form>`** track pending and error state and revalidate the routes you name on success. `router.revalidate()` / `revalidate(href)` bust the cache after a mutation.
159
+
160
+ ## Rendering and SEO
161
+
162
+ 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.
163
+
164
+ ```ts
165
+ // toil.config.ts
166
+ export default defineConfig({
167
+ client: {
168
+ seo: {
169
+ url: 'https://example.com',
170
+ title: 'My App',
171
+ openGraph: { siteName: 'My App', type: 'website', image: '/og.png' },
172
+ twitter: { card: 'summary_large_image' },
173
+ jsonLd: { '@context': 'https://schema.org', '@type': 'WebSite' },
174
+ themeColor: '#2563ff', // also the Discord / Slack embed accent
175
+ preconnect: ['https://cdn.example.com'],
176
+ robots: { ai: 'allow' },
177
+ },
178
+ },
179
+ });
180
+ ```
181
+
182
+ - **Per-route `metadata`** (or `generateMetadata` derived from the loader's data) wins per page over layout defaults.
183
+ - **Static prerender** writes a `<route>/index.html` for every static route with that route's head baked in. **SSG** enumerates dynamic routes via `generateStaticParams` and bakes per-URL HTML.
184
+ - **`robots.txt`**, **`sitemap.xml`**, and **`llms.txt`** generated together.
185
+ - Full **OpenGraph**, **Twitter card**, **JSON-LD**, **canonical**, **theme-color**, and **`preconnect` / `dns-prefetch`** early hints. Output is XSS-hardened.
186
+
187
+ ### AI search, on by default
188
+
189
+ Turn on SEO and the build emits an `llms.txt` describing your site for language models, plus a `robots.txt` with explicit per-bot rules. 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:
190
+
191
+ ```ts
192
+ seo: { llms: { instructions: 'Docs live at /docs.' }, robots: { ai: 'disallow' } }
193
+ ```
194
+
195
+ Your users get search too. The compiler bakes a static index of every page's title, description, and keywords, and `Toil.usePageSearch` (or the pure `searchPages`) returns ranked, navigable results.
196
+
197
+ ## Assets and the Vite build
198
+
199
+ ToilJS owns Vite, for the dev server and for the ahead-of-time production build, and does the boring optimization for you. The build tells you what it did:
200
+
201
+ ```
202
+ $ npm run build
203
+ ✓ optimized 3 images
204
+ client/hero.png
205
+ → images/hero.webp 148.0 kB → 19.3 kB -87%
206
+ ✓ preloaded 2 fonts
207
+ fonts/inter-latin.woff2 24.10 kB
208
+ ```
209
+
210
+ - **Images** (Vite imagetools + sharp): every imported raster is compressed to webp, with resize and reformat via `?w=400;800&format=webp&as=srcset`. The build logs the savings.
211
+ - **Fonts**: bundled `@font-face` fonts get a `<link rel="preload">` so text paints sooner, also logged.
212
+ - **Chunking**: React is split into its own long-lived chunk; assets land in tidy `images/`, `fonts/`, and `css/` folders.
213
+ - **Node polyfills** (`Buffer`, `global`, `process`) for libraries that expect them.
214
+ - **Styling**: plain CSS out of the box, with Sass, Less, Stylus, and Tailwind v4 a `toiljs configure` away.
215
+
216
+ You never write an `index.html`, a `main.tsx`, or a Vite config. The framework generates and owns them.
217
+
218
+ ## Components
219
+
220
+ Zero-import, on the `Toil` global:
221
+
222
+ - **`Image`** drops in for `<img>`: reserves space (no layout shift), lazy-loads, async-decodes, `priority` for the LCP image, `fill` + `objectFit`, optional blur placeholder.
223
+ - **`Script`** loads external or inline scripts with a `strategy` (`afterInteractive` / `lazyOnload` / `beforeInteractive`), deduplicated so a script never runs twice.
224
+ - **`Form`** submits to an action without a reload, revalidates on success, exposes pending state, optionally resets fields.
225
+ - **`Slot`** renders a parallel `@slot` route, the basis for modal overlays.
226
+ - **`Head`** / **`useHead`** / **`useTitle`** set the title and `<meta>` / `<link>` tags imperatively and compose across the tree.
227
+
228
+ ## Realtime
229
+
230
+ One typed channel API for live data in both directions.
231
+
232
+ ```tsx
233
+ const messages = Toil.useChannel<Message>('/chat');
234
+ ```
235
+
236
+ `connectChannel` / `useChannel` / `resolveChannelUrl` handle connection, reconnection, and message decoding, text or binary frames. Today the channel runs over WebSocket. Next it rides **WebTransport** over HTTP/3: multiplexed bidirectional streams and datagrams over QUIC with no head-of-line blocking. Toil is built to be the **first framework to ship first-class WebTransport**, with automatic WebSocket fallback, so the same `useChannel` quietly upgrades to the fastest transport the client and network support, with no code change.
237
+
238
+ ## The server: ToilScript + WebAssembly
239
+
240
+ Your backend is written in **ToilScript**, a TypeScript-syntax language that compiles to WebAssembly. You write request handlers; the compiler produces a single portable `.wasm` module.
241
+
242
+ ```ts
243
+ // server/HelloHandler.ts
244
+ export class HelloHandler extends ToilHandler {
245
+ handle(req: Request): Response {
246
+ if (req.path == '/api/hello') return Response.json('{"hello":"world"}');
247
+ return Response.notFound();
248
+ }
249
+ }
250
+ ```
251
+
252
+ - **REST handlers**: `Request` (method, path, headers, body) in, `Response` out, with `text` / `html` / `json` / `notFound` / `badRequest` / `internalError` helpers and the full set of HTTP methods.
253
+ - **Binary IO on both sides**: `DataWriter`, `DataReader`, `FastMap`, and `FastSet` are shared client and server globals (and `toiljs/io`), so you can move structured bytes instead of paying the JSON tax.
254
+ - **Typed RPC (preview)**: tag a server function and the compiler generates a typed `Server.*` surface on the client, end to end, no hand-written glue. The typed pipeline is in place today; the network transport is landing next.
255
+
256
+ `toiljs start` self-hosts the built client and a WebSocket channel on [hyper-express](https://github.com/kartikk221/hyper-express) (backed by uWebSockets.js) for local and small deployments. For where this is headed at scale, see [The road to hyperscale](#the-road-to-hyperscale).
257
+
258
+ ## Agentic tooling
259
+
260
+ `toiljs dev` injects a floating toolbar (stripped from production builds entirely, no flag, no leftover bytes) that surfaces the framework's live state: 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 toggles for view and loader transitions. Press **Cmd/Ctrl+K** for a command palette to jump to any route or run a dev action.
261
+
262
+ It also ships an **AI tab**: hand off the current page's context (and its source) to Claude or ChatGPT in one click, or wire a provider for inline answers. The API key is read server-side only and never reaches the browser.
263
+
264
+ ```ts
265
+ import { defineConfig, AiProvider } from 'toiljs/compiler';
266
+
267
+ export default defineConfig({
268
+ client: {
269
+ devtools: {
270
+ ai: {
271
+ provider: AiProvider.Anthropic, // or AiProvider.OpenAI, or a custom endpoint
272
+ model: 'claude-sonnet-4-6',
273
+ apiKeyEnv: 'ANTHROPIC_API_KEY', // read by the dev server only
274
+ },
275
+ },
276
+ },
277
+ });
278
+ ```
279
+
280
+ And the `toiljs create` wizard scaffolds assistant files (CLAUDE.md, AGENTS.md, Cursor, and Copilot configs) so your repo is ready for coding agents on day one.
281
+
282
+ ## The toolkit is the standard
283
+
284
+ ToilJS sets the toolchain so nobody argues about it. Strict TypeScript, ESLint (typescript-eslint, react-hooks, react-refresh, @eslint-react), and Prettier come configured and enforced from the first commit, shipped as `toiljs/tsconfig`, `toiljs/eslint`, and `toiljs/prettier`. New apps extend them automatically and can init git in the same step. Opt in to as much as you want, nothing to copy, nothing to bikeshed.
285
+
286
+ ## Configuration
287
+
288
+ One file, `toil.config.ts`, typed with `defineConfig`. Every option has a sensible default, so most apps only set `seo`.
289
+
290
+ ```ts
291
+ import { defineConfig } from 'toiljs/compiler';
292
+
293
+ export default defineConfig({
294
+ client: {
295
+ srcDir: 'client', // source root (default: client)
296
+ routesDir: 'routes', // route folder (default: routes)
297
+ base: '/', // base path (default: /)
298
+ port: 3000, // dev / start port (default: 3000)
299
+ images: true, // webp + resize pipeline (default: true)
300
+ fonts: true, // preload bundled fonts (default: true)
301
+ viewTransitions: false, // animated navigation (default: false)
302
+ transitions: false, // keep page during load (default: false)
303
+ devtools: true, // dev toolbar, or { ai } (default: true)
304
+ seo: {
305
+ // url, title, openGraph, twitter, jsonLd, robots, themeColor, ...
306
+ },
307
+ vite: {
308
+ // escape hatch: a full Vite InlineConfig, merged on top
309
+ },
310
+ },
311
+ });
312
+ ```
313
+
314
+ Set any feature to `false` to turn it off. `client.vite` is merged into the generated Vite config for the rare case you need to reach the bundler directly.
315
+
316
+ ## CLI
317
+
318
+ ```
319
+ toiljs create [name] scaffold a new app
320
+ toiljs dev dev server with HMR
321
+ toiljs build ahead-of-time production build
322
+ toiljs start self-host the built client + realtime channel
323
+ toiljs configure toggle styling and asset features on an existing app
324
+ toiljs doctor diagnose project setup and dependencies
325
+ toiljs update check for and apply dependency updates
326
+ ```
327
+
328
+ - **`create [name]`** runs an interactive wizard: template (`app` or `minimal`), CSS flavor (`css` / `sass` / `less` / `stylus`), Tailwind, which AI assistant files to scaffold, image optimization, git init, and package manager (`npm` / `pnpm` / `yarn` / `bun`). Every prompt has a flag (`--template`, `--style`, `--tailwind`, `--ai`, `--images`, `--git`, `--install`, `--pm`), and `--yes` runs it non-interactively.
329
+ - **`dev`** starts the Vite dev server with HMR and regenerates the route table as you add or remove files. `--port` to override.
330
+ - **`build`** produces the optimized static client: prerendered HTML, `sitemap.xml`, `robots.txt`, `llms.txt`, and compressed images and fonts.
331
+ - **`start`** self-hosts the build and a realtime channel at `/_toil` on hyper-express. `--port` (default 3000), `--host` (pass `0.0.0.0` to expose on the network).
332
+ - **`configure`** edits an existing app: switch CSS preprocessor, toggle Tailwind or image optimization, and sync dependencies.
333
+ - **`doctor`** runs read-only checks across environment, routing, config, assets, and the server build, with a fix hint on each. `--json` for CI; it exits non-zero when a check fails.
334
+ - **`update`** checks the registry and groups available updates by major / minor / patch; pick what to apply or pass `-y` for all (`--target` sets the strategy).
335
+
336
+ ## One file does a lot
337
+
338
+ ```tsx
339
+ // client/routes/posts/[id].tsx -> /posts/:id
340
+ interface Post {
341
+ title: string;
342
+ }
343
+
344
+ export const metadata: Toil.Metadata = { title: 'Post' }; // SEO, baked into the HTML at build
345
+
346
+ export const loader = async ({ params }: Toil.LoaderArgs): Promise<Post> => {
347
+ const res = await fetch(`/api/posts/${params.id}`); // runs before render, no useEffect
348
+ return res.json();
349
+ };
350
+
351
+ export default function PostPage() {
352
+ const post = Toil.useLoaderData(loader); // typed Post, no generics
353
+ return (
354
+ <article>
355
+ <h1>{post.title}</h1>
356
+ <Toil.Link href="/posts">All posts</Toil.Link> {/* href is type-checked */}
357
+ </article>
358
+ );
359
+ }
360
+ ```
361
+
362
+ No imports. `Toil` is a fully-typed global, tree-shaken at build. The page renders with its data already loaded.
363
+
364
+ ## Architecture
365
+
366
+ One pipeline, from your editor to planetary scale. The build pipeline and the React client ship today; the edge runtime, the ToilDB data layer, and Dacely Cloud are the roadmap (marked in purple).
367
+
368
+ ```mermaid
369
+ flowchart TB
370
+ classDef today fill:#0e1520,stroke:#2563ff,stroke-width:2px,color:#cfe0ff;
371
+ classDef soon fill:#160f1f,stroke:#7c3aed,stroke-width:2px,color:#ead7ff,stroke-dasharray:6 4;
372
+
373
+ subgraph BUILD["BUILD, toiljs CLI, shipping today"]
374
+ direction TB
375
+ TSX["client/ &nbsp; React + TSX routes"] --> VITE["Vite<br/>HMR + ahead-of-time build"] --> ART["static client<br/>prerendered HTML<br/>sitemap, robots, llms<br/>optimized images + fonts"]
376
+ TS["server/ &nbsp; ToilScript .ts"] --> TSC["toilscript compiler"] --> WASM["one .wasm module"]
377
+ TOOL["toolkit: TypeScript, ESLint, Prettier, git<br/>dev toolbar: routes, loader cache, head/OG, errors<br/>AI tab to Claude / ChatGPT, Cmd-K palette<br/>typed RPC surface Server.*"]
378
+ end
379
+
380
+ CLIENTS["CLIENTS<br/>browsers, mobile, API clients, AI crawlers / LLMs"]
381
+
382
+ subgraph EDGE["EDGE, anycast, multi-region POPs, scale-out, roadmap"]
383
+ direction LR
384
+ SC["STATIC CLIENT<br/>React SPA + baked HTML<br/>images, fonts, css<br/>served from CDN / edge"]
385
+ RT["WASM EDGE RUNTIME<br/>your ToilScript server as one .wasm<br/>isolated per-core tenant at line rate<br/>Request to handler to Response<br/>realtime channels, binary IO<br/>x many tenants x many POPs"]
386
+ SC <-->|"typed RPC&nbsp; Server.*"| RT
387
+ end
388
+
389
+ subgraph DATA["ToilDB, edge-replicated typed data, roadmap"]
390
+ direction LR
391
+ MODES["collections<br/>owned, eventLog, counter, set<br/>unique, escrow, snapshot<br/>local reads fast, CRDT writes merge everywhere<br/>owned writes fast at owner, global claims slow"]
392
+ DUR["durable store<br/>structured records"]
393
+ BLOB["blob store<br/>large objects"]
394
+ MODES --> DUR
395
+ MODES --> BLOB
396
+ end
397
+
398
+ CTRL["DACELY CLOUD, control plane, roadmap<br/>deploy, distribute, scale<br/>client to the edge, .wasm to the runtime, data replicated"]
399
+
400
+ ART -->|deploys| SC
401
+ WASM -->|deploys| RT
402
+ CLIENTS -->|"HTTP/1.1 + WebSocket today<br/>HTTP/3, QUIC, WebTransport roadmap<br/>TLS today, post-quantum roadmap"| EDGE
403
+ RT -->|"region-replicated"| DATA
404
+ CTRL -. orchestrates .-> EDGE
405
+ CTRL -. orchestrates .-> DATA
406
+
407
+ class TSX,VITE,ART,TS,TSC,WASM,TOOL,CLIENTS today;
408
+ class SC,RT,MODES,DUR,BLOB,CTRL soon;
409
+ ```
410
+
411
+ <details>
412
+ <summary>Same diagram as plain ASCII (for npm and text-only views)</summary>
413
+
414
+ ```
415
+ ┌──────────────────────────────────────────────────────────────────────────┐
416
+ │ BUILD toiljs CLI [today] │
417
+ │ │
418
+ │ client/ React + TSX routes ──▶ Vite (HMR ahead-of-time build) ──┐ │
419
+ │ server/ ToilScript (.ts) ──▶ toilscript compiler ──▶ one .wasm │ │
420
+ │ │ │
421
+ │ toolkit TypeScript ESLint Prettier git (opt-in presets) │ │
422
+ │ dev toolbar: routes loader cache head/OG errors │ │
423
+ │ AI tab → Claude / ChatGPT ⌘K palette │ │
424
+ │ emits static client prerendered HTML sitemap robots llms │ │
425
+ │ optimized images / fonts typed RPC surface (Server.*) │ │
426
+ └───────────────────────────────────────────────────────────────┬─────┬───┘
427
+ deploys ▼ │ │ ▼
428
+ ┌──────────────────────────────────────────────────────────────────────────┐
429
+ │ CLIENTS │
430
+ │ browsers mobile webviews API clients AI crawlers/LLMs │
431
+ └───────────────────────────────────┬──────────────────────────────────────┘
432
+ HTTP/1.1 + WebSocket [today] │ HTTP/3 QUIC WebTransport [soon]
433
+ TLS [today] │ post-quantum transport [soon]
434
+
435
+ ╔══════════════════════════════════════════════════════════════════════════╗
436
+ ║ EDGE anycast multi-region POPs scale-out [soon] ║
437
+ ║ ║
438
+ ║ ┌─────────────────────────┐ ┌─────────────────────────────────┐ ║
439
+ ║ │ STATIC CLIENT │ │ WASM EDGE RUNTIME │ ║
440
+ ║ │ React SPA + baked HTML │ │ your ToilScript server as one │ ║
441
+ ║ │ images fonts css │ typed│ .wasm, run as an isolated │ ║
442
+ ║ │ served from CDN / edge │◀──RPC─▶│ per-core tenant at line rate │ ║
443
+ ║ │ │ Server.* Request ▶ handler ▶ Response │ ║
444
+ ║ │ llms robots sitemap │ │ realtime channels binary IO │ ║
445
+ ║ └─────────────────────────┘ └───────────────┬─────────────────┘ ║
446
+ ║ instant, cacheable × many tenants × many POPs ║
447
+ ╚═══════════════════════════════════════════════════════╪══════════════════╝
448
+ ▼ region-replicated
449
+ ╔══════════════════════════════════════════════════════════════════════════╗
450
+ ║ ToilDB edge-replicated, typed data layer [soon] ║
451
+ ║ ║
452
+ ║ collections: owned eventLog counter set unique escrow ║
453
+ ║ snapshot (the method name tells you the cost) ║
454
+ ║ local reads fast CRDT writes merge everywhere ║
455
+ ║ owned writes fast at the owner global claims explicitly slow ║
456
+ ║ ║
457
+ ║ ┌──────────────────────┐ ┌──────────────────────┐ ║
458
+ ║ │ durable store │ │ blob store │ ║
459
+ ║ │ structured records │ │ large objects │ ║
460
+ ║ └──────────────────────┘ └──────────────────────┘ ║
461
+ ╚═══════════════════════════════════════════════════════╪══════════════════╝
462
+
463
+ ╔══════════════════════════════════════════════════════════════════════════╗
464
+ ║ DACELY CLOUD control plane: deploy distribute scale [soon] ║
465
+ ║ push your app ─▶ client to the edge, .wasm to the runtime, data replicated ║
466
+ ╚══════════════════════════════════════════════════════════════════════════╝
467
+
468
+ [today] shipping now [soon] architecture + roadmap, not yet GA
469
+ ```
470
+
471
+ </details>
472
+
473
+ ---
474
+
475
+ ## The road to hyperscale
476
+
477
+ > **Architecture and roadmap.** This section is where Toil is going, not what ships in the box today. The framework above is real and usable now; the platform below is the design it is being built toward.
478
+
479
+ The reason the client is static and the server is WebAssembly is that the WebAssembly runs on a runtime engineered from scratch for the edge. Toil's backend treats your compiled server as an isolated tenant and is built to serve it at line rate, so the same app that runs on your laptop is designed to scale out across the edge without a rewrite.
480
+
481
+ <div align="center">
482
+
483
+ ![runtime](https://img.shields.io/badge/runtime-WASM_edge-2563ff?style=for-the-badge)
484
+ ![transport](https://img.shields.io/badge/transport-HTTP%2F3_+_WebTransport-654ff0?style=for-the-badge)
485
+ ![data](https://img.shields.io/badge/data-edge_replicated-22e3ab?style=for-the-badge)
486
+ ![security](https://img.shields.io/badge/security-post--quantum-7c3aed?style=for-the-badge)
487
+
488
+ </div>
489
+
490
+ - **A purpose-built WebAssembly edge runtime.** Your server runs as an isolated WebAssembly tenant on a runtime engineered for line-rate, multi-gigabit throughput and per-tenant isolation, not on a general-purpose Node process.
491
+ - **HTTP/3 and WebTransport.** Bidirectional streams and datagrams over QUIC for interactive, multiplexed realtime, beyond the WebSocket channel that ships today.
492
+ - **ToilDB, an edge-replicated data layer.** Typed collections declared in ToilScript, where the method name tells you the cost: local reads are fast, appends and CRDT counters/sets merge everywhere, owned writes are fast at the owner, and rare global claims are explicitly slow. Hyperscale data, without a per-query consistency knob to get wrong.
493
+ - **Post-quantum-ready transport.** Forward-looking encryption for the edge as the QUIC layer lands.
494
+ - **Dacely Cloud.** Managed hosting for the whole stack: push your app, the static client goes to the edge and your WebAssembly server runs on the runtime above.
495
+
496
+ This is the spine the framework was shaped around. Today you write a typed, file-based React app with a WebAssembly server; the roadmap is the platform that runs it at planetary scale.
497
+
498
+ ## Tech
499
+
500
+ <div align="center">
501
+
502
+ <img src="https://img.shields.io/badge/React_19-20232a?style=for-the-badge&logo=react&logoColor=61dafb" alt="React 19" />
503
+ <img src="https://img.shields.io/badge/TypeScript-3178c6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
504
+ <img src="https://img.shields.io/badge/Vite-646cff?style=for-the-badge&logo=vite&logoColor=white" alt="Vite" />
505
+ <img src="https://img.shields.io/badge/WebAssembly-654ff0?style=for-the-badge&logo=webassembly&logoColor=white" alt="WebAssembly" />
506
+ <img src="https://img.shields.io/badge/ToilScript-cb9820?style=for-the-badge&logoColor=white" alt="ToilScript" />
507
+ <img src="https://img.shields.io/badge/sharp-99cc00?style=for-the-badge&logo=sharp&logoColor=white" alt="sharp" />
508
+ <img src="https://img.shields.io/badge/ESLint-4b32c3?style=for-the-badge&logo=eslint&logoColor=white" alt="ESLint" />
509
+ <img src="https://img.shields.io/badge/Prettier-f7b93e?style=for-the-badge&logo=prettier&logoColor=black" alt="Prettier" />
510
+ <img src="https://img.shields.io/badge/Tailwind_v4-06b6d4?style=for-the-badge&logo=tailwindcss&logoColor=white" alt="Tailwind v4" />
511
+
512
+ </div>
513
+
514
+ React 19, TypeScript, Vite, [ToilScript](https://www.npmjs.com/package/toilscript) (TypeScript syntax, compiles to WebAssembly), Vite imagetools + sharp, ESLint (typescript-eslint, react-hooks, react-refresh, @eslint-react), Prettier, Tailwind v4 (optional).
515
+
516
+ ## Start
517
+
518
+ ```bash
519
+ npx toiljs create my-app
520
+ ```
521
+
522
+ Everything in the framework half of this README is already on. You just build the app.
523
+
524
+ <div align="center"><br/><sub>Apache-2.0, <a href="https://toil.org">toil.org</a></sub></div>