toiljs 0.0.115 → 0.0.117

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 (326) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +16 -7
  3. package/TYPESCRIPT_LAW.md +1946 -2073
  4. package/build/backend/.tsbuildinfo +1 -1
  5. package/build/cli/.tsbuildinfo +1 -1
  6. package/build/cli/index.js +2375 -258
  7. package/build/client/.tsbuildinfo +1 -1
  8. package/build/client/dev/devtools.js +1 -1
  9. package/build/client/dev/error-overlay.js +1 -1
  10. package/build/client/routing/mount.js +1 -1
  11. package/build/compiler/.tsbuildinfo +1 -1
  12. package/build/compiler/auth-emails.js +7 -3
  13. package/build/compiler/emails.js +1 -1
  14. package/build/compiler/index.d.ts +1 -0
  15. package/build/compiler/index.js +25 -4
  16. package/build/compiler/pages.js +2 -3
  17. package/build/compiler/prerender.d.ts +3 -8
  18. package/build/compiler/prerender.js +88 -127
  19. package/build/compiler/ssg.js +3 -4
  20. package/build/compiler/template-build.js +3 -4
  21. package/build/compiler/toil-docs.generated.js +6 -5
  22. package/build/compiler/vite.js +3 -6
  23. package/build/devserver/.tsbuildinfo +1 -1
  24. package/build/devserver/db/database.js +7 -3
  25. package/build/devserver/db/types.js +1 -1
  26. package/build/devserver/production.js +4 -3
  27. package/build/devserver/runtime/crypto.d.ts +1 -1
  28. package/build/io/.tsbuildinfo +1 -1
  29. package/build/logger/.tsbuildinfo +1 -1
  30. package/build/shared/.tsbuildinfo +1 -1
  31. package/examples/basic/client/routes/analytics.tsx +7 -5
  32. package/examples/basic/server/tsconfig.json +1 -3
  33. package/package.json +45 -44
  34. package/presets/no-uint8array-tostring.js +120 -153
  35. package/presets/oxlint.d.ts +5 -0
  36. package/presets/oxlint.js +224 -0
  37. package/server/runtime/tsconfig.json +1 -3
  38. package/tsconfig.json +2 -11
  39. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  40. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -90
  41. package/.github/ISSUE_TEMPLATE/config.yml +0 -8
  42. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  43. package/.github/PULL_REQUEST_TEMPLATE.md +0 -43
  44. package/.github/changelog-config.json +0 -45
  45. package/.github/dependabot.yml +0 -27
  46. package/.github/workflows/ci.yml +0 -222
  47. package/.vscode/settings.json +0 -10
  48. package/docs/README.md +0 -107
  49. package/docs/auth/README.md +0 -104
  50. package/docs/auth/configuration.md +0 -105
  51. package/docs/auth/emails.md +0 -92
  52. package/docs/auth/extending.md +0 -202
  53. package/docs/auth/how-it-works.md +0 -138
  54. package/docs/auth/usage.md +0 -188
  55. package/docs/backend/README.md +0 -143
  56. package/docs/backend/data.md +0 -351
  57. package/docs/backend/rest.md +0 -402
  58. package/docs/backend/rpc.md +0 -226
  59. package/docs/background/README.md +0 -114
  60. package/docs/background/daemons.md +0 -276
  61. package/docs/background/derive.md +0 -179
  62. package/docs/cli/README.md +0 -379
  63. package/docs/concepts/ai-guide.md +0 -132
  64. package/docs/concepts/config.md +0 -416
  65. package/docs/concepts/decorators.md +0 -127
  66. package/docs/concepts/security.md +0 -108
  67. package/docs/concepts/tiers.md +0 -166
  68. package/docs/concepts/types.md +0 -216
  69. package/docs/database/README.md +0 -143
  70. package/docs/database/capacity.md +0 -350
  71. package/docs/database/counters.md +0 -174
  72. package/docs/database/documents.md +0 -277
  73. package/docs/database/events.md +0 -307
  74. package/docs/database/membership.md +0 -246
  75. package/docs/database/setup.md +0 -155
  76. package/docs/database/unique.md +0 -216
  77. package/docs/database/views.md +0 -246
  78. package/docs/frontend/README.md +0 -104
  79. package/docs/frontend/components.md +0 -286
  80. package/docs/frontend/data-fetching.md +0 -260
  81. package/docs/frontend/images.md +0 -148
  82. package/docs/frontend/metadata.md +0 -344
  83. package/docs/frontend/navigation.md +0 -296
  84. package/docs/frontend/rendering.md +0 -236
  85. package/docs/frontend/routing.md +0 -346
  86. package/docs/frontend/scripts.md +0 -118
  87. package/docs/frontend/search.md +0 -191
  88. package/docs/frontend/styling.md +0 -147
  89. package/docs/frontend/toil-global.md +0 -216
  90. package/docs/getting-started/README.md +0 -81
  91. package/docs/getting-started/create-project.md +0 -131
  92. package/docs/getting-started/deploy.md +0 -101
  93. package/docs/getting-started/first-app.md +0 -215
  94. package/docs/getting-started/installation.md +0 -124
  95. package/docs/getting-started/migrating.md +0 -125
  96. package/docs/getting-started/project-structure.md +0 -163
  97. package/docs/introduction/README.md +0 -66
  98. package/docs/introduction/design-principles.md +0 -81
  99. package/docs/introduction/distributed.md +0 -91
  100. package/docs/introduction/how-it-works.md +0 -121
  101. package/docs/introduction/hyperscale.md +0 -70
  102. package/docs/introduction/modern-stack.md +0 -85
  103. package/docs/introduction/vs-other-frameworks.md +0 -64
  104. package/docs/introduction/why-toil.md +0 -99
  105. package/docs/llms.txt +0 -95
  106. package/docs/realtime/README.md +0 -102
  107. package/docs/realtime/channels.md +0 -211
  108. package/docs/realtime/streams.md +0 -369
  109. package/docs/services/README.md +0 -60
  110. package/docs/services/analytics.md +0 -279
  111. package/docs/services/caching.md +0 -175
  112. package/docs/services/cookies.md +0 -235
  113. package/docs/services/crypto.md +0 -209
  114. package/docs/services/email.md +0 -289
  115. package/docs/services/environment.md +0 -141
  116. package/docs/services/ratelimit.md +0 -174
  117. package/docs/services/time.md +0 -85
  118. package/eslint.config.js +0 -57
  119. package/examples/basic/client/routes/docs/[...slug].tsx +0 -12
  120. package/presets/eslint.js +0 -88
  121. package/src/backend/index.ts +0 -204
  122. package/src/cli/configure.ts +0 -363
  123. package/src/cli/create.ts +0 -924
  124. package/src/cli/db.ts +0 -158
  125. package/src/cli/diagnostics.ts +0 -665
  126. package/src/cli/doctor.ts +0 -982
  127. package/src/cli/features.ts +0 -170
  128. package/src/cli/index.ts +0 -326
  129. package/src/cli/notify.ts +0 -120
  130. package/src/cli/proc.ts +0 -56
  131. package/src/cli/ui.ts +0 -169
  132. package/src/cli/update.ts +0 -248
  133. package/src/cli/updates.ts +0 -92
  134. package/src/cli/validate.ts +0 -31
  135. package/src/cli/version-check.ts +0 -125
  136. package/src/client/auth.ts +0 -869
  137. package/src/client/channel/channel.ts +0 -146
  138. package/src/client/components/Form.tsx +0 -65
  139. package/src/client/components/Image.tsx +0 -160
  140. package/src/client/components/Script.tsx +0 -113
  141. package/src/client/components/Slot.tsx +0 -21
  142. package/src/client/dev/devtools.tsx +0 -1089
  143. package/src/client/dev/error-overlay.tsx +0 -225
  144. package/src/client/errors.ts +0 -11
  145. package/src/client/head/head.ts +0 -182
  146. package/src/client/head/metadata.ts +0 -110
  147. package/src/client/index.ts +0 -130
  148. package/src/client/navigation/Link.tsx +0 -108
  149. package/src/client/navigation/NavLink.tsx +0 -86
  150. package/src/client/navigation/navigation.ts +0 -235
  151. package/src/client/navigation/prefetch.ts +0 -169
  152. package/src/client/navigation/scroll.ts +0 -53
  153. package/src/client/routing/Router.tsx +0 -216
  154. package/src/client/routing/action.ts +0 -122
  155. package/src/client/routing/error-boundary.tsx +0 -43
  156. package/src/client/routing/hooks.ts +0 -140
  157. package/src/client/routing/lazy.ts +0 -77
  158. package/src/client/routing/loader.ts +0 -381
  159. package/src/client/routing/match.ts +0 -47
  160. package/src/client/routing/mount.tsx +0 -90
  161. package/src/client/routing/params-context.ts +0 -10
  162. package/src/client/routing/slot-context.ts +0 -7
  163. package/src/client/rpc.ts +0 -97
  164. package/src/client/search/search.ts +0 -189
  165. package/src/client/search/use-page-search.ts +0 -73
  166. package/src/client/ssr/markers.tsx +0 -172
  167. package/src/client/stream/client.ts +0 -237
  168. package/src/client/types.ts +0 -73
  169. package/src/compiler/auth-emails.ts +0 -312
  170. package/src/compiler/config.ts +0 -369
  171. package/src/compiler/docs.ts +0 -127
  172. package/src/compiler/email-preview.ts +0 -314
  173. package/src/compiler/emails.ts +0 -404
  174. package/src/compiler/fonts.ts +0 -89
  175. package/src/compiler/generate.ts +0 -471
  176. package/src/compiler/image-blur.ts +0 -42
  177. package/src/compiler/image-report.ts +0 -88
  178. package/src/compiler/index.ts +0 -1362
  179. package/src/compiler/pages.ts +0 -49
  180. package/src/compiler/plugin.ts +0 -391
  181. package/src/compiler/prerender.ts +0 -263
  182. package/src/compiler/routes.ts +0 -170
  183. package/src/compiler/seo.ts +0 -415
  184. package/src/compiler/sri.ts +0 -144
  185. package/src/compiler/ssg.ts +0 -177
  186. package/src/compiler/ssr-codegen.ts +0 -85
  187. package/src/compiler/template-build.ts +0 -676
  188. package/src/compiler/template.ts +0 -262
  189. package/src/compiler/toil-docs.generated.ts +0 -77
  190. package/src/compiler/vite.ts +0 -245
  191. package/src/devserver/analytics/index.ts +0 -317
  192. package/src/devserver/config/dotenv.ts +0 -102
  193. package/src/devserver/config/env.ts +0 -30
  194. package/src/devserver/config/ratelimit.ts +0 -123
  195. package/src/devserver/daemon/catalog.ts +0 -120
  196. package/src/devserver/daemon/cron.ts +0 -87
  197. package/src/devserver/daemon/host.ts +0 -134
  198. package/src/devserver/daemon/index.ts +0 -369
  199. package/src/devserver/daemon/runtime.ts +0 -48
  200. package/src/devserver/db/catalog.ts +0 -109
  201. package/src/devserver/db/database.ts +0 -1834
  202. package/src/devserver/db/derives.ts +0 -121
  203. package/src/devserver/db/index.ts +0 -20
  204. package/src/devserver/db/routeKinds.ts +0 -191
  205. package/src/devserver/db/types.ts +0 -168
  206. package/src/devserver/email/caps.ts +0 -0
  207. package/src/devserver/email/config.ts +0 -141
  208. package/src/devserver/email/console.ts +0 -348
  209. package/src/devserver/email/index.ts +0 -155
  210. package/src/devserver/email/providers.ts +0 -131
  211. package/src/devserver/email/status.ts +0 -23
  212. package/src/devserver/email/validate.ts +0 -37
  213. package/src/devserver/email/wire.ts +0 -55
  214. package/src/devserver/http/cache.ts +0 -0
  215. package/src/devserver/http/envelope.ts +0 -157
  216. package/src/devserver/http/proxy.ts +0 -172
  217. package/src/devserver/http/runtime.ts +0 -387
  218. package/src/devserver/index.ts +0 -43
  219. package/src/devserver/production-ipc.ts +0 -63
  220. package/src/devserver/production-worker.ts +0 -83
  221. package/src/devserver/production.ts +0 -718
  222. package/src/devserver/runtime/crypto.ts +0 -587
  223. package/src/devserver/runtime/host.ts +0 -427
  224. package/src/devserver/runtime/module.ts +0 -442
  225. package/src/devserver/server.ts +0 -251
  226. package/src/devserver/ssr.ts +0 -208
  227. package/src/devserver/stream/catalog.ts +0 -106
  228. package/src/devserver/stream/host.ts +0 -42
  229. package/src/devserver/stream/index.ts +0 -301
  230. package/src/devserver/stream/manager.ts +0 -167
  231. package/src/devserver/stream/router.ts +0 -101
  232. package/src/devserver/stream/wire.ts +0 -58
  233. package/src/devserver/stream/ws.ts +0 -78
  234. package/src/devserver/wasm/sections.ts +0 -59
  235. package/src/devserver/wasm/surface.ts +0 -115
  236. package/src/io/FastMap.ts +0 -151
  237. package/src/io/FastSet.ts +0 -113
  238. package/src/io/codec.ts +0 -362
  239. package/src/io/index.ts +0 -10
  240. package/src/io/lengths.ts +0 -14
  241. package/src/io/types.ts +0 -19
  242. package/src/logger/index.ts +0 -22
  243. package/src/shared/index.ts +0 -46
  244. package/test/analytics-dev.test.ts +0 -113
  245. package/test/assembly/aspect-shim.ts +0 -33
  246. package/test/assembly/cookie.spec.ts +0 -303
  247. package/test/assembly/example.spec.ts +0 -22
  248. package/test/assembly/ssr.spec.ts +0 -95
  249. package/test/assembly.test.ts +0 -59
  250. package/test/auth-emails.test.ts +0 -118
  251. package/test/built-ssr.test.ts +0 -148
  252. package/test/channel.test.ts +0 -21
  253. package/test/config-host-port.test.ts +0 -59
  254. package/test/configure.test.ts +0 -94
  255. package/test/daemon-build.test.ts +0 -278
  256. package/test/daemon-catalog.test.ts +0 -349
  257. package/test/daemon-emulation.test.ts +0 -256
  258. package/test/db.test.ts +0 -0
  259. package/test/devserver-database.test.ts +0 -1102
  260. package/test/devserver-email.test.ts +0 -306
  261. package/test/devserver-pqauth.test.ts +0 -93
  262. package/test/devserver-secrets.test.ts +0 -63
  263. package/test/devserver.test.ts +0 -259
  264. package/test/doctor.test.ts +0 -298
  265. package/test/dom/Image.test.tsx +0 -144
  266. package/test/dom/Link.test.tsx +0 -67
  267. package/test/dom/NavLink.test.tsx +0 -37
  268. package/test/dom/Script.test.tsx +0 -48
  269. package/test/dom/action.test.tsx +0 -146
  270. package/test/dom/error-overlay.test.tsx +0 -44
  271. package/test/dom/loader.test.tsx +0 -121
  272. package/test/dom/navigation.test.ts +0 -59
  273. package/test/dom/revalidate.test.tsx +0 -38
  274. package/test/dom/route-head.test.tsx +0 -56
  275. package/test/dom/router-loading.test.tsx +0 -44
  276. package/test/dom/scroll.test.ts +0 -56
  277. package/test/dom/slot.test.tsx +0 -131
  278. package/test/dom/use-metadata.test.tsx +0 -58
  279. package/test/dom/view-transitions.test.tsx +0 -53
  280. package/test/dynamic-routing.test.ts +0 -132
  281. package/test/email-console.test.ts +0 -86
  282. package/test/email-preview.test.ts +0 -73
  283. package/test/emails.test.ts +0 -58
  284. package/test/errors.test.ts +0 -21
  285. package/test/example-guestbook.test.ts +0 -123
  286. package/test/features.test.ts +0 -149
  287. package/test/fixtures/bignum-wire/spec.ts +0 -25
  288. package/test/fixtures/daemon-app.ts +0 -65
  289. package/test/fixtures/stream-echo.ts +0 -26
  290. package/test/fixtures/stream-gate.ts +0 -28
  291. package/test/fixtures/stream-trap.ts +0 -18
  292. package/test/fixtures/stream-typed.ts +0 -41
  293. package/test/fonts.test.ts +0 -28
  294. package/test/global-setup.ts +0 -17
  295. package/test/head.test.ts +0 -42
  296. package/test/image-blur.test.ts +0 -50
  297. package/test/io.test.ts +0 -117
  298. package/test/metadata.test.ts +0 -40
  299. package/test/navlink.test.ts +0 -28
  300. package/test/pages.test.ts +0 -105
  301. package/test/placeholder.test.ts +0 -9
  302. package/test/pqauth-e2e.test.ts +0 -216
  303. package/test/pqauth-email.test.ts +0 -801
  304. package/test/prerender.test.ts +0 -116
  305. package/test/prettier-plugin.test.ts +0 -46
  306. package/test/routes.test.ts +0 -76
  307. package/test/rpc-bignum-wire.test.ts +0 -164
  308. package/test/rpc-dispatch.test.ts +0 -132
  309. package/test/rpc-kinds.test.ts +0 -18
  310. package/test/rpc.test.ts +0 -66
  311. package/test/search.test.ts +0 -114
  312. package/test/seo.test.ts +0 -175
  313. package/test/slot-layouts.test.ts +0 -69
  314. package/test/sri.test.ts +0 -129
  315. package/test/ssg.test.ts +0 -36
  316. package/test/ssr-hydration.test.tsx +0 -122
  317. package/test/ssr-render.test.ts +0 -197
  318. package/test/ssr-template.test.tsx +0 -482
  319. package/test/stream-emulation.test.ts +0 -479
  320. package/test/ui.test.ts +0 -40
  321. package/test/update.test.ts +0 -58
  322. package/test/validate.test.ts +0 -42
  323. package/test/version-check.test.ts +0 -130
  324. package/tests/data-parity/generated-parity.ts +0 -99
  325. package/tests/data-parity/parity.ts +0 -80
  326. package/tests/data-parity/spec.ts +0 -46
@@ -1,85 +0,0 @@
1
- # Time
2
-
3
- **`Time` is the edge's clock: the blessed way to read the current time in your backend.** It gives you the number of milliseconds or seconds since a fixed reference point, so you can stamp events and compute expiries.
4
-
5
- ## How to use it
6
-
7
- `Time` is an ambient global (no import needed), and is also exported from `toiljs/server/runtime`:
8
-
9
- ```ts
10
- const ms = Time.nowMillis(); // milliseconds since the Unix epoch (a u64)
11
- const s = Time.nowSeconds(); // whole seconds since the Unix epoch (a u64)
12
- ```
13
-
14
- | Member | Returns | Description |
15
- | --- | --- | --- |
16
- | `Time.nowMillis()` | `u64` | Milliseconds since the **Unix epoch** (midnight UTC on 1 January 1970). This is the same value as JavaScript's `Date.now()`. |
17
- | `Time.nowSeconds()` | `u64` | Whole seconds since the epoch (`nowMillis() / 1000`). This is the unit sessions and login challenges use. |
18
-
19
- The **Unix epoch** is just the agreed "time zero" that computers count from. "Milliseconds since the epoch" is a single number that unambiguously names an instant, with no time zone to worry about.
20
-
21
- ## Why time is a "host import" (and what that means)
22
-
23
- Your backend runs as a **WebAssembly** module: a small, sandboxed program. A sandbox is deliberately sealed off from the outside world. It has no clock of its own, no network, no files, nothing except the memory it was given and a short list of functions the host chooses to hand it. Each of those handed-in functions is called a **host import**.
24
-
25
- This is on purpose. WebAssembly aims to be **deterministic**: given the same inputs, the same code produces the same outputs, every time. That makes it safe to run untrusted code, easy to cache, and easy to reason about. But the current time is not deterministic: it is different every time you ask. So a WebAssembly module cannot just "know" the time; it has to **ask the host** for it. Reading the clock is a call across the sandbox boundary.
26
-
27
- `Time` is the thin, friendly wrapper over that call. Under the hood it uses the host's `Date.now()` binding (`env.Date.now`).
28
-
29
- ```mermaid
30
- flowchart LR
31
- subgraph Sandbox["Your backend (WebAssembly sandbox)"]
32
- C["Time.nowMillis()"]
33
- end
34
- subgraph Host["Dacely edge host"]
35
- D["env.Date.now"]
36
- K["real system clock"]
37
- end
38
- C -->|host import| D
39
- D --> K
40
- K -->|milliseconds| C
41
- ```
42
-
43
- Both the edge and the dev server provide this binding, so time behaves **identically** in `toiljs dev` and in production.
44
-
45
- ## `Time` vs calling `Date.now()`
46
-
47
- ToilScript's `Date.now()` lowers to the exact same host import, so you *can* call it directly. Prefer `Time`:
48
-
49
- - It makes the host boundary (and the single millisecond unit) explicit and easy to find in a codebase.
50
- - It gives you `nowSeconds()` without writing an open-coded `/ 1000` at every call site.
51
-
52
- Both are the same clock, so timing you do in a handler lines up with timing the framework does. For example, the auth system uses `Time.nowSeconds()` for session issue and expiry times.
53
-
54
- ## Correct usage: timestamps and TTLs
55
-
56
- `Time` is **wall-clock time**, exactly like a browser's `Date.now()`. It follows the system clock, which means it can occasionally **step backward** (for instance when the machine corrects its clock against a time server).
57
-
58
- Do:
59
-
60
- - **Stamp an instant.** Record when something happened, so you can compare it later: an event time, a "last seen" marker.
61
- - **Compute an expiry (a TTL).** A **TTL** ("time to live") is a deadline. You set it by adding a duration to *now*, and you check it by comparing *now* against the stored deadline:
62
-
63
- ```ts
64
- const ttlSeconds: u64 = 3600; // valid for one hour
65
- const expiresAt = Time.nowSeconds() + ttlSeconds;
66
-
67
- // ...later, on another request...
68
- const expired = Time.nowSeconds() >= expiresAt;
69
- ```
70
-
71
- Do not:
72
-
73
- - **Measure elapsed time with it.** Do not use it as a stopwatch to time how long an operation took. Because the wall clock can step backward, `end - start` could come out zero or negative. `Time` answers "what instant is it now?", not "how much time has passed?".
74
-
75
- ## Gotchas
76
-
77
- - **It is wall-clock, not monotonic.** It can jump backward across a clock correction. Fine for timestamps and expiries; wrong for measuring durations.
78
- - **Milliseconds vs seconds.** `nowMillis()` and `nowSeconds()` differ by a factor of 1000. Mixing them (comparing a millisecond stamp to a second stamp) is a common bug. Sessions and login challenges use **seconds**.
79
- - **UTC only.** These are epoch counts, with no time zone. Format for display on the client if you need a local time.
80
-
81
- ## Related
82
-
83
- - [Auth, sessions, and `@user`](../auth/usage.md): sessions use `Time.nowSeconds()` for their issue and expiry stamps.
84
- - [Caching](./caching.md): TTLs are the same idea, a deadline after which a saved value is stale.
85
- - [Compute tiers](../concepts/tiers.md): more on the WebAssembly sandbox your backend runs in.
package/eslint.config.js DELETED
@@ -1,57 +0,0 @@
1
- // @ts-check
2
-
3
- import eslint from '@eslint/js';
4
- import tseslint from 'typescript-eslint';
5
-
6
- export default tseslint.config(
7
- // toilscript server (WASM) + its ambient std + build output are not part of the TS project.
8
- // toil-docs.generated.ts is a build artifact (gitignored, emitted by gen:docs) — not linted.
9
- {
10
- ignores: [
11
- 'build/**',
12
- 'server/**',
13
- 'std/server/**',
14
- 'toil-env.d.ts',
15
- 'src/compiler/toil-docs.generated.ts',
16
- ],
17
- },
18
- eslint.configs.recommended,
19
- ...tseslint.configs.strictTypeChecked,
20
- {
21
- languageOptions: {
22
- parserOptions: {
23
- projectService: true,
24
- tsconfigDirName: import.meta.dirname,
25
- },
26
- },
27
- rules: {
28
- 'no-undef': 'off',
29
- '@typescript-eslint/no-unused-vars': 'off',
30
- 'no-empty': 'off',
31
- '@typescript-eslint/restrict-template-expressions': 'off',
32
- '@typescript-eslint/only-throw-error': 'off',
33
- '@typescript-eslint/no-unnecessary-condition': 'off',
34
- '@typescript-eslint/unbound-method': 'warn',
35
- '@typescript-eslint/no-confusing-void-expression': 'off',
36
- '@typescript-eslint/no-extraneous-class': 'off',
37
- 'no-async-promise-executor': 'off',
38
- '@typescript-eslint/no-misused-promises': 'off',
39
- '@typescript-eslint/no-unnecessary-type-parameters': 'off',
40
- '@typescript-eslint/no-duplicate-enum-values': 'off',
41
- 'prefer-spread': 'off',
42
- '@typescript-eslint/no-empty-object-type': 'off',
43
- '@typescript-eslint/no-base-to-string': 'off',
44
- '@typescript-eslint/no-dynamic-delete': 'off',
45
- '@typescript-eslint/no-redundant-type-constituents': 'off',
46
-
47
- // This is stupid. null and undefined is not the same thing. who ever made this eslint rule is stupid.
48
- 'no-useless-assignment': 'off',
49
- '@typescript-eslint/no-unnecessary-type-arguments': 'off',
50
- '@typescript-eslint/no-unnecessary-type-assertion': 'off',
51
- },
52
- },
53
- {
54
- files: ['**/*.js'],
55
- ...tseslint.configs.disableTypeChecked,
56
- },
57
- );
@@ -1,12 +0,0 @@
1
- export default function Docs() {
2
- const { slug } = Toil.useParams();
3
- return (
4
- <main>
5
- <h1>Docs</h1>
6
- <p>
7
- Catch-all route <code>client/routes/docs/[...slug].tsx</code> matched: <code>{slug}</code>
8
- </p>
9
- <Toil.Link href="/">Back home</Toil.Link>
10
- </main>
11
- );
12
- }
package/presets/eslint.js DELETED
@@ -1,88 +0,0 @@
1
- /** toiljs shared ESLint flat config: `import toiljs from 'toiljs/eslint'; export default toiljs;` */
2
- import eslintReact from '@eslint-react/eslint-plugin';
3
- import eslint from '@eslint/js';
4
- import reactHooks from 'eslint-plugin-react-hooks';
5
- import reactRefresh from 'eslint-plugin-react-refresh';
6
- import tseslint from 'typescript-eslint';
7
-
8
- import noUint8ArrayToString from './no-uint8array-tostring.js';
9
-
10
- export default tseslint.config(
11
- { ignores: ['dist', 'build', '.toil', 'node_modules', 'toil-env.d.ts', 'server/**'] },
12
- {
13
- extends: [
14
- eslint.configs.recommended,
15
- ...tseslint.configs.recommended,
16
- ...tseslint.configs.strictTypeChecked,
17
- ],
18
- files: ['**/*.{ts,tsx}'],
19
- languageOptions: {
20
- ecmaVersion: 2023,
21
- parserOptions: {
22
- projectService: true,
23
- },
24
- },
25
- plugins: {
26
- 'react-hooks': reactHooks,
27
- 'react-refresh': reactRefresh,
28
- '@eslint-react': eslintReact,
29
- custom: noUint8ArrayToString,
30
- },
31
- rules: {
32
- ...reactHooks.configs.recommended.rules,
33
- // Route files conventionally export `loader` / `revalidate` / `metadata` /
34
- // `generateMetadata` / `searchHints` alongside the default component; the toil compiler
35
- // consumes them at runtime/build. Allow them (plus primitive constants) so Fast Refresh
36
- // doesn't flag the pattern.
37
- 'react-refresh/only-export-components': [
38
- 'warn',
39
- {
40
- allowConstantExport: true,
41
- allowExportNames: [
42
- 'loader',
43
- 'revalidate',
44
- 'metadata',
45
- 'generateMetadata',
46
- 'searchHints',
47
- ],
48
- },
49
- ],
50
- 'no-undef': 'off',
51
- '@typescript-eslint/no-unused-vars': 'off',
52
- 'no-empty': 'off',
53
- '@typescript-eslint/restrict-template-expressions': 'off',
54
- '@typescript-eslint/only-throw-error': 'off',
55
- '@typescript-eslint/no-unnecessary-condition': 'off',
56
- '@typescript-eslint/unbound-method': 'warn',
57
- '@typescript-eslint/no-confusing-void-expression': 'off',
58
- '@typescript-eslint/no-extraneous-class': 'off',
59
- 'no-async-promise-executor': 'off',
60
- '@typescript-eslint/no-misused-promises': 'off',
61
- '@typescript-eslint/no-unnecessary-type-parameters': 'off',
62
- '@typescript-eslint/no-duplicate-enum-values': 'off',
63
- 'prefer-spread': 'off',
64
- '@typescript-eslint/no-empty-object-type': 'off',
65
- '@typescript-eslint/no-floating-promises': 'off',
66
- '@typescript-eslint/ban-ts-comment': 'off',
67
- 'no-constant-binary-expression': 'off',
68
- 'no-useless-assignment': 'off',
69
- '@typescript-eslint/no-unsafe-assignment': 'off',
70
- '@typescript-eslint/no-unsafe-call': 'off',
71
- '@typescript-eslint/no-unsafe-member-access': 'off',
72
- '@typescript-eslint/no-unsafe-argument': 'off',
73
- '@typescript-eslint/no-unnecessary-type-conversion': 'warn',
74
- 'react-hooks/set-state-in-effect': 'warn',
75
- 'custom/no-uint8array-tostring': 'error',
76
- 'padding-line-between-statements': [
77
- 'error',
78
- { blankLine: 'always', prev: 'block-like', next: '*' },
79
- ],
80
- '@typescript-eslint/no-deprecated': 'off',
81
- '@typescript-eslint/no-unnecessary-type-arguments': 'off',
82
- },
83
- },
84
- {
85
- files: ['**/*.js'],
86
- ...tseslint.configs.disableTypeChecked,
87
- },
88
- );
@@ -1,204 +0,0 @@
1
- /**
2
- * toiljs backend, the self-host / dev server, built on @dacely/hyper-express (uWebSockets.js)
3
- * for very high throughput. It serves the built client (static assets + SPA fallback) and exposes
4
- * a WebSocket channel for realtime / live updates.
5
- *
6
- * This is the Node "server" that hosts the app on a local machine; it is distinct from the
7
- * toilscript WASM runtime in `server/runtime` (the `toiljs/server/runtime` library export).
8
- */
9
- import fs from 'node:fs';
10
- import path from 'node:path';
11
-
12
- import {
13
- type MiddlewareNext,
14
- type Request,
15
- type Response,
16
- Server,
17
- type Websocket,
18
- } from '@dacely/hyper-express';
19
-
20
- const DEFAULT_MAX_BODY_LENGTH = 1024 * 1024 * 8;
21
- const MAX_BODY_BUFFER = 1024 * 32;
22
- const HTTP_IDLE_TIMEOUT = 60;
23
- const HTTP_RESPONSE_TIMEOUT = 120;
24
-
25
- const WS_MAX_PAYLOAD_LENGTH = 1024 * 1024;
26
- const WS_IDLE_TIMEOUT = 120;
27
- const WS_MAX_BACKPRESSURE = 1024 * 1024 * 2;
28
-
29
- const CORS_METHODS = 'GET, POST, OPTIONS, PUT, PATCH, DELETE';
30
- const CORS_HEADERS = 'X-Requested-With, content-type';
31
-
32
- /** Options for {@link startBackend}. */
33
- export interface BackendOptions {
34
- /** Directory to serve (the built client `outDir`, e.g. `dist`). */
35
- readonly root: string;
36
- /** Listening port. Default `3000`. */
37
- readonly port?: number;
38
- /**
39
- * Bind host. Default `127.0.0.1` (loopback only). Pass `0.0.0.0` (or a specific interface) to
40
- * expose the server on the network; do so deliberately, since the WebSocket channel relays
41
- * messages between all connected clients.
42
- */
43
- readonly host?: string;
44
- /**
45
- * Extra origins allowed to open the WebSocket channel, in addition to the server's own origin.
46
- * Cross-origin WebSocket handshakes from other origins are rejected (prevents cross-site
47
- * WebSocket hijacking). Example: `['https://app.example.com']`.
48
- */
49
- readonly allowedOrigins?: readonly string[];
50
- /** WebSocket channel path. Default `/_toil`. */
51
- readonly wsPath?: string;
52
- /** Send permissive CORS headers + handle preflight. Default `true`. */
53
- readonly cors?: boolean;
54
- /** Max request body length in bytes. Default 8 MB. */
55
- readonly maxBodyLength?: number;
56
- }
57
-
58
- /** A running backend instance. */
59
- export interface RunningBackend {
60
- readonly port: number;
61
- readonly host: string;
62
- readonly wsPath: string;
63
- /** Sends a message to every connected WebSocket client. */
64
- broadcast(message: string): void;
65
- /** Number of currently-connected WebSocket clients. */
66
- clientCount(): number;
67
- /** Gracefully shuts the server down. */
68
- close(): Promise<void>;
69
- }
70
-
71
- /**
72
- * Whether a WebSocket upgrade from `origin` may connect. A missing `Origin` (non-browser clients
73
- * like curl or server-to-server) is allowed; a browser `Origin` must match the host the server was
74
- * reached at (same-origin) or be in the explicit allowlist. This blocks cross-site WebSocket
75
- * hijacking, where a page the victim visits opens a socket to this server from their browser.
76
- */
77
- function isWsOriginAllowed(
78
- origin: string | undefined,
79
- hostHeader: string | undefined,
80
- allowed: readonly string[] | undefined,
81
- ): boolean {
82
- if (!origin) return true;
83
- if (allowed?.includes(origin)) return true;
84
- try {
85
- return new URL(origin).host === hostHeader;
86
- } catch {
87
- return false;
88
- }
89
- }
90
-
91
- /** Resolves a request path to a file inside `root`, guarding against path traversal. */
92
- function resolveStaticFile(root: string, requestPath: string): string | null {
93
- const decoded = decodeURIComponent(requestPath);
94
- const resolved = path.join(root, decoded);
95
- if (resolved !== root && !resolved.startsWith(root + path.sep)) return null;
96
- if (decoded === '/' || decoded === '') return null;
97
- if (fs.existsSync(resolved) && fs.statSync(resolved).isFile()) return resolved;
98
- return null;
99
- }
100
-
101
- /**
102
- * Starts the hyper-express server serving `root` with an SPA fallback to `index.html`,
103
- * plus a WebSocket channel at `wsPath`. Resolves once the server is listening.
104
- */
105
- export async function startBackend(options: BackendOptions): Promise<RunningBackend> {
106
- const port = options.port ?? 3000;
107
- const host = options.host ?? '127.0.0.1';
108
- const wsPath = options.wsPath ?? '/_toil';
109
- const cors = options.cors ?? true;
110
- const root = path.resolve(options.root);
111
- const indexHtml = path.join(root, 'index.html');
112
-
113
- const app = new Server({
114
- max_body_length: options.maxBodyLength ?? DEFAULT_MAX_BODY_LENGTH,
115
- max_body_buffer: MAX_BODY_BUFFER,
116
- fast_abort: true,
117
- idle_timeout: HTTP_IDLE_TIMEOUT,
118
- response_timeout: HTTP_RESPONSE_TIMEOUT,
119
- });
120
-
121
- const clients = new Set<Websocket>();
122
-
123
- app.set_error_handler((_request: Request, response: Response, _error: Error) => {
124
- if (response.completed) return;
125
- response.atomic(() => {
126
- response.status(500).json({ error: 'Internal server error.' });
127
- });
128
- });
129
-
130
- if (cors) {
131
- app.use((request: Request, response: Response, next: MiddlewareNext) => {
132
- if (request.method !== 'OPTIONS') {
133
- response.setHeader('Access-Control-Allow-Origin', '*');
134
- response.setHeader('Access-Control-Allow-Methods', CORS_METHODS);
135
- response.setHeader('Access-Control-Allow-Headers', CORS_HEADERS);
136
- }
137
- response.removeHeader('uWebSockets');
138
- next();
139
- });
140
- app.options('/*', (_request: Request, response: Response) => {
141
- response.setHeader('Access-Control-Allow-Origin', '*');
142
- response.setHeader('Access-Control-Allow-Methods', CORS_METHODS);
143
- response.setHeader('Access-Control-Allow-Headers', CORS_HEADERS);
144
- response.setHeader('Access-Control-Max-Age', '86400');
145
- response.status(204).send();
146
- });
147
- }
148
-
149
- app.ws(
150
- wsPath,
151
- {
152
- message_type: 'String',
153
- max_payload_length: WS_MAX_PAYLOAD_LENGTH,
154
- idle_timeout: WS_IDLE_TIMEOUT,
155
- max_backpressure: WS_MAX_BACKPRESSURE,
156
- },
157
- (ws) => {
158
- clients.add(ws);
159
- ws.send(JSON.stringify({ type: 'connected', clients: clients.size }));
160
- ws.on('message', (message: string) => {
161
- for (const client of clients) client.send(message);
162
- });
163
- ws.on('drain', () => {});
164
- ws.on('close', () => {
165
- clients.delete(ws);
166
- });
167
- },
168
- );
169
-
170
- // Gate the WebSocket upgrade on the request Origin, so a cross-origin page in a victim's browser
171
- // cannot hijack the channel (CSWSH). Registered AFTER `app.ws` so it overrides that route's
172
- // default upgrade handler (hyper-express links it to the companion ws route). Same-origin and
173
- // non-browser clients pass; others get 403.
174
- app.upgrade(wsPath, (request: Request, response: Response) => {
175
- if (
176
- !isWsOriginAllowed(request.headers.origin, request.headers.host, options.allowedOrigins)
177
- ) {
178
- response.status(403).send();
179
- return;
180
- }
181
- response.upgrade({});
182
- });
183
-
184
- app.get('/*', (request: Request, response: Response) => {
185
- if (response.completed) return;
186
- const file = resolveStaticFile(root, request.path);
187
- response.sendFile(file ?? indexHtml);
188
- });
189
-
190
- await app.listen(port, host);
191
-
192
- return {
193
- port,
194
- host,
195
- wsPath,
196
- broadcast: (message: string): void => {
197
- for (const client of clients) client.send(message);
198
- },
199
- clientCount: (): number => clients.size,
200
- close: async (): Promise<void> => {
201
- await app.shutdown();
202
- },
203
- };
204
- }