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.
- package/CHANGELOG.md +10 -0
- package/README.md +16 -7
- package/TYPESCRIPT_LAW.md +1946 -2073
- package/build/backend/.tsbuildinfo +1 -1
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/index.js +2375 -258
- package/build/client/.tsbuildinfo +1 -1
- package/build/client/dev/devtools.js +1 -1
- package/build/client/dev/error-overlay.js +1 -1
- package/build/client/routing/mount.js +1 -1
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/auth-emails.js +7 -3
- package/build/compiler/emails.js +1 -1
- package/build/compiler/index.d.ts +1 -0
- package/build/compiler/index.js +25 -4
- package/build/compiler/pages.js +2 -3
- package/build/compiler/prerender.d.ts +3 -8
- package/build/compiler/prerender.js +88 -127
- package/build/compiler/ssg.js +3 -4
- package/build/compiler/template-build.js +3 -4
- package/build/compiler/toil-docs.generated.js +6 -5
- package/build/compiler/vite.js +3 -6
- package/build/devserver/.tsbuildinfo +1 -1
- package/build/devserver/db/database.js +7 -3
- package/build/devserver/db/types.js +1 -1
- package/build/devserver/production.js +4 -3
- package/build/devserver/runtime/crypto.d.ts +1 -1
- package/build/io/.tsbuildinfo +1 -1
- package/build/logger/.tsbuildinfo +1 -1
- package/build/shared/.tsbuildinfo +1 -1
- package/examples/basic/client/routes/analytics.tsx +7 -5
- package/examples/basic/server/tsconfig.json +1 -3
- package/package.json +45 -44
- package/presets/no-uint8array-tostring.js +120 -153
- package/presets/oxlint.d.ts +5 -0
- package/presets/oxlint.js +224 -0
- package/server/runtime/tsconfig.json +1 -3
- package/tsconfig.json +2 -11
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -90
- package/.github/ISSUE_TEMPLATE/config.yml +0 -8
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -43
- package/.github/changelog-config.json +0 -45
- package/.github/dependabot.yml +0 -27
- package/.github/workflows/ci.yml +0 -222
- package/.vscode/settings.json +0 -10
- package/docs/README.md +0 -107
- package/docs/auth/README.md +0 -104
- package/docs/auth/configuration.md +0 -105
- package/docs/auth/emails.md +0 -92
- package/docs/auth/extending.md +0 -202
- package/docs/auth/how-it-works.md +0 -138
- package/docs/auth/usage.md +0 -188
- package/docs/backend/README.md +0 -143
- package/docs/backend/data.md +0 -351
- package/docs/backend/rest.md +0 -402
- package/docs/backend/rpc.md +0 -226
- package/docs/background/README.md +0 -114
- package/docs/background/daemons.md +0 -276
- package/docs/background/derive.md +0 -179
- package/docs/cli/README.md +0 -379
- package/docs/concepts/ai-guide.md +0 -132
- package/docs/concepts/config.md +0 -416
- package/docs/concepts/decorators.md +0 -127
- package/docs/concepts/security.md +0 -108
- package/docs/concepts/tiers.md +0 -166
- package/docs/concepts/types.md +0 -216
- package/docs/database/README.md +0 -143
- package/docs/database/capacity.md +0 -350
- package/docs/database/counters.md +0 -174
- package/docs/database/documents.md +0 -277
- package/docs/database/events.md +0 -307
- package/docs/database/membership.md +0 -246
- package/docs/database/setup.md +0 -155
- package/docs/database/unique.md +0 -216
- package/docs/database/views.md +0 -246
- package/docs/frontend/README.md +0 -104
- package/docs/frontend/components.md +0 -286
- package/docs/frontend/data-fetching.md +0 -260
- package/docs/frontend/images.md +0 -148
- package/docs/frontend/metadata.md +0 -344
- package/docs/frontend/navigation.md +0 -296
- package/docs/frontend/rendering.md +0 -236
- package/docs/frontend/routing.md +0 -346
- package/docs/frontend/scripts.md +0 -118
- package/docs/frontend/search.md +0 -191
- package/docs/frontend/styling.md +0 -147
- package/docs/frontend/toil-global.md +0 -216
- package/docs/getting-started/README.md +0 -81
- package/docs/getting-started/create-project.md +0 -131
- package/docs/getting-started/deploy.md +0 -101
- package/docs/getting-started/first-app.md +0 -215
- package/docs/getting-started/installation.md +0 -124
- package/docs/getting-started/migrating.md +0 -125
- package/docs/getting-started/project-structure.md +0 -163
- package/docs/introduction/README.md +0 -66
- package/docs/introduction/design-principles.md +0 -81
- package/docs/introduction/distributed.md +0 -91
- package/docs/introduction/how-it-works.md +0 -121
- package/docs/introduction/hyperscale.md +0 -70
- package/docs/introduction/modern-stack.md +0 -85
- package/docs/introduction/vs-other-frameworks.md +0 -64
- package/docs/introduction/why-toil.md +0 -99
- package/docs/llms.txt +0 -95
- package/docs/realtime/README.md +0 -102
- package/docs/realtime/channels.md +0 -211
- package/docs/realtime/streams.md +0 -369
- package/docs/services/README.md +0 -60
- package/docs/services/analytics.md +0 -279
- package/docs/services/caching.md +0 -175
- package/docs/services/cookies.md +0 -235
- package/docs/services/crypto.md +0 -209
- package/docs/services/email.md +0 -289
- package/docs/services/environment.md +0 -141
- package/docs/services/ratelimit.md +0 -174
- package/docs/services/time.md +0 -85
- package/eslint.config.js +0 -57
- package/examples/basic/client/routes/docs/[...slug].tsx +0 -12
- package/presets/eslint.js +0 -88
- package/src/backend/index.ts +0 -204
- package/src/cli/configure.ts +0 -363
- package/src/cli/create.ts +0 -924
- package/src/cli/db.ts +0 -158
- package/src/cli/diagnostics.ts +0 -665
- package/src/cli/doctor.ts +0 -982
- package/src/cli/features.ts +0 -170
- package/src/cli/index.ts +0 -326
- package/src/cli/notify.ts +0 -120
- package/src/cli/proc.ts +0 -56
- package/src/cli/ui.ts +0 -169
- package/src/cli/update.ts +0 -248
- package/src/cli/updates.ts +0 -92
- package/src/cli/validate.ts +0 -31
- package/src/cli/version-check.ts +0 -125
- package/src/client/auth.ts +0 -869
- package/src/client/channel/channel.ts +0 -146
- package/src/client/components/Form.tsx +0 -65
- package/src/client/components/Image.tsx +0 -160
- package/src/client/components/Script.tsx +0 -113
- package/src/client/components/Slot.tsx +0 -21
- package/src/client/dev/devtools.tsx +0 -1089
- package/src/client/dev/error-overlay.tsx +0 -225
- package/src/client/errors.ts +0 -11
- package/src/client/head/head.ts +0 -182
- package/src/client/head/metadata.ts +0 -110
- package/src/client/index.ts +0 -130
- package/src/client/navigation/Link.tsx +0 -108
- package/src/client/navigation/NavLink.tsx +0 -86
- package/src/client/navigation/navigation.ts +0 -235
- package/src/client/navigation/prefetch.ts +0 -169
- package/src/client/navigation/scroll.ts +0 -53
- package/src/client/routing/Router.tsx +0 -216
- package/src/client/routing/action.ts +0 -122
- package/src/client/routing/error-boundary.tsx +0 -43
- package/src/client/routing/hooks.ts +0 -140
- package/src/client/routing/lazy.ts +0 -77
- package/src/client/routing/loader.ts +0 -381
- package/src/client/routing/match.ts +0 -47
- package/src/client/routing/mount.tsx +0 -90
- package/src/client/routing/params-context.ts +0 -10
- package/src/client/routing/slot-context.ts +0 -7
- package/src/client/rpc.ts +0 -97
- package/src/client/search/search.ts +0 -189
- package/src/client/search/use-page-search.ts +0 -73
- package/src/client/ssr/markers.tsx +0 -172
- package/src/client/stream/client.ts +0 -237
- package/src/client/types.ts +0 -73
- package/src/compiler/auth-emails.ts +0 -312
- package/src/compiler/config.ts +0 -369
- package/src/compiler/docs.ts +0 -127
- package/src/compiler/email-preview.ts +0 -314
- package/src/compiler/emails.ts +0 -404
- package/src/compiler/fonts.ts +0 -89
- package/src/compiler/generate.ts +0 -471
- package/src/compiler/image-blur.ts +0 -42
- package/src/compiler/image-report.ts +0 -88
- package/src/compiler/index.ts +0 -1362
- package/src/compiler/pages.ts +0 -49
- package/src/compiler/plugin.ts +0 -391
- package/src/compiler/prerender.ts +0 -263
- package/src/compiler/routes.ts +0 -170
- package/src/compiler/seo.ts +0 -415
- package/src/compiler/sri.ts +0 -144
- package/src/compiler/ssg.ts +0 -177
- package/src/compiler/ssr-codegen.ts +0 -85
- package/src/compiler/template-build.ts +0 -676
- package/src/compiler/template.ts +0 -262
- package/src/compiler/toil-docs.generated.ts +0 -77
- package/src/compiler/vite.ts +0 -245
- package/src/devserver/analytics/index.ts +0 -317
- package/src/devserver/config/dotenv.ts +0 -102
- package/src/devserver/config/env.ts +0 -30
- package/src/devserver/config/ratelimit.ts +0 -123
- package/src/devserver/daemon/catalog.ts +0 -120
- package/src/devserver/daemon/cron.ts +0 -87
- package/src/devserver/daemon/host.ts +0 -134
- package/src/devserver/daemon/index.ts +0 -369
- package/src/devserver/daemon/runtime.ts +0 -48
- package/src/devserver/db/catalog.ts +0 -109
- package/src/devserver/db/database.ts +0 -1834
- package/src/devserver/db/derives.ts +0 -121
- package/src/devserver/db/index.ts +0 -20
- package/src/devserver/db/routeKinds.ts +0 -191
- package/src/devserver/db/types.ts +0 -168
- package/src/devserver/email/caps.ts +0 -0
- package/src/devserver/email/config.ts +0 -141
- package/src/devserver/email/console.ts +0 -348
- package/src/devserver/email/index.ts +0 -155
- package/src/devserver/email/providers.ts +0 -131
- package/src/devserver/email/status.ts +0 -23
- package/src/devserver/email/validate.ts +0 -37
- package/src/devserver/email/wire.ts +0 -55
- package/src/devserver/http/cache.ts +0 -0
- package/src/devserver/http/envelope.ts +0 -157
- package/src/devserver/http/proxy.ts +0 -172
- package/src/devserver/http/runtime.ts +0 -387
- package/src/devserver/index.ts +0 -43
- package/src/devserver/production-ipc.ts +0 -63
- package/src/devserver/production-worker.ts +0 -83
- package/src/devserver/production.ts +0 -718
- package/src/devserver/runtime/crypto.ts +0 -587
- package/src/devserver/runtime/host.ts +0 -427
- package/src/devserver/runtime/module.ts +0 -442
- package/src/devserver/server.ts +0 -251
- package/src/devserver/ssr.ts +0 -208
- package/src/devserver/stream/catalog.ts +0 -106
- package/src/devserver/stream/host.ts +0 -42
- package/src/devserver/stream/index.ts +0 -301
- package/src/devserver/stream/manager.ts +0 -167
- package/src/devserver/stream/router.ts +0 -101
- package/src/devserver/stream/wire.ts +0 -58
- package/src/devserver/stream/ws.ts +0 -78
- package/src/devserver/wasm/sections.ts +0 -59
- package/src/devserver/wasm/surface.ts +0 -115
- package/src/io/FastMap.ts +0 -151
- package/src/io/FastSet.ts +0 -113
- package/src/io/codec.ts +0 -362
- package/src/io/index.ts +0 -10
- package/src/io/lengths.ts +0 -14
- package/src/io/types.ts +0 -19
- package/src/logger/index.ts +0 -22
- package/src/shared/index.ts +0 -46
- package/test/analytics-dev.test.ts +0 -113
- package/test/assembly/aspect-shim.ts +0 -33
- package/test/assembly/cookie.spec.ts +0 -303
- package/test/assembly/example.spec.ts +0 -22
- package/test/assembly/ssr.spec.ts +0 -95
- package/test/assembly.test.ts +0 -59
- package/test/auth-emails.test.ts +0 -118
- package/test/built-ssr.test.ts +0 -148
- package/test/channel.test.ts +0 -21
- package/test/config-host-port.test.ts +0 -59
- package/test/configure.test.ts +0 -94
- package/test/daemon-build.test.ts +0 -278
- package/test/daemon-catalog.test.ts +0 -349
- package/test/daemon-emulation.test.ts +0 -256
- package/test/db.test.ts +0 -0
- package/test/devserver-database.test.ts +0 -1102
- package/test/devserver-email.test.ts +0 -306
- package/test/devserver-pqauth.test.ts +0 -93
- package/test/devserver-secrets.test.ts +0 -63
- package/test/devserver.test.ts +0 -259
- package/test/doctor.test.ts +0 -298
- package/test/dom/Image.test.tsx +0 -144
- package/test/dom/Link.test.tsx +0 -67
- package/test/dom/NavLink.test.tsx +0 -37
- package/test/dom/Script.test.tsx +0 -48
- package/test/dom/action.test.tsx +0 -146
- package/test/dom/error-overlay.test.tsx +0 -44
- package/test/dom/loader.test.tsx +0 -121
- package/test/dom/navigation.test.ts +0 -59
- package/test/dom/revalidate.test.tsx +0 -38
- package/test/dom/route-head.test.tsx +0 -56
- package/test/dom/router-loading.test.tsx +0 -44
- package/test/dom/scroll.test.ts +0 -56
- package/test/dom/slot.test.tsx +0 -131
- package/test/dom/use-metadata.test.tsx +0 -58
- package/test/dom/view-transitions.test.tsx +0 -53
- package/test/dynamic-routing.test.ts +0 -132
- package/test/email-console.test.ts +0 -86
- package/test/email-preview.test.ts +0 -73
- package/test/emails.test.ts +0 -58
- package/test/errors.test.ts +0 -21
- package/test/example-guestbook.test.ts +0 -123
- package/test/features.test.ts +0 -149
- package/test/fixtures/bignum-wire/spec.ts +0 -25
- package/test/fixtures/daemon-app.ts +0 -65
- package/test/fixtures/stream-echo.ts +0 -26
- package/test/fixtures/stream-gate.ts +0 -28
- package/test/fixtures/stream-trap.ts +0 -18
- package/test/fixtures/stream-typed.ts +0 -41
- package/test/fonts.test.ts +0 -28
- package/test/global-setup.ts +0 -17
- package/test/head.test.ts +0 -42
- package/test/image-blur.test.ts +0 -50
- package/test/io.test.ts +0 -117
- package/test/metadata.test.ts +0 -40
- package/test/navlink.test.ts +0 -28
- package/test/pages.test.ts +0 -105
- package/test/placeholder.test.ts +0 -9
- package/test/pqauth-e2e.test.ts +0 -216
- package/test/pqauth-email.test.ts +0 -801
- package/test/prerender.test.ts +0 -116
- package/test/prettier-plugin.test.ts +0 -46
- package/test/routes.test.ts +0 -76
- package/test/rpc-bignum-wire.test.ts +0 -164
- package/test/rpc-dispatch.test.ts +0 -132
- package/test/rpc-kinds.test.ts +0 -18
- package/test/rpc.test.ts +0 -66
- package/test/search.test.ts +0 -114
- package/test/seo.test.ts +0 -175
- package/test/slot-layouts.test.ts +0 -69
- package/test/sri.test.ts +0 -129
- package/test/ssg.test.ts +0 -36
- package/test/ssr-hydration.test.tsx +0 -122
- package/test/ssr-render.test.ts +0 -197
- package/test/ssr-template.test.tsx +0 -482
- package/test/stream-emulation.test.ts +0 -479
- package/test/ui.test.ts +0 -40
- package/test/update.test.ts +0 -58
- package/test/validate.test.ts +0 -42
- package/test/version-check.test.ts +0 -130
- package/tests/data-parity/generated-parity.ts +0 -99
- package/tests/data-parity/parity.ts +0 -80
- package/tests/data-parity/spec.ts +0 -46
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
# Your first app
|
|
2
|
-
|
|
3
|
-
Build a tiny feature end to end: a page with a "Like" button that reads and writes a real number in ToilDB, so the count survives page reloads and restarts. Along the way you meet the typed client, an HTTP route, and the database, the three pieces you use in almost every toiljs app.
|
|
4
|
-
|
|
5
|
-
## What you will build
|
|
6
|
-
|
|
7
|
-
A `/likes` page that shows a running like count and a button to add one. The count lives in ToilDB, so it is shared by everyone and it persists.
|
|
8
|
-
|
|
9
|
-
```mermaid
|
|
10
|
-
sequenceDiagram
|
|
11
|
-
participant B as Browser (/likes page)
|
|
12
|
-
participant SH as shared/server.ts (typed client)
|
|
13
|
-
participant W as server.wasm (@rest route)
|
|
14
|
-
participant DB as ToilDB (counter)
|
|
15
|
-
B->>SH: Server.REST.likes.add()
|
|
16
|
-
SH->>W: POST /likes
|
|
17
|
-
W->>DB: counter.add(key, 1)
|
|
18
|
-
W-->>SH: { count: 43 }
|
|
19
|
-
SH-->>B: typed LikeCount
|
|
20
|
-
B->>B: show "43 likes"
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Before you start
|
|
24
|
-
|
|
25
|
-
You need a project. If you do not have one yet, create one and start the dev server:
|
|
26
|
-
|
|
27
|
-
```sh
|
|
28
|
-
toiljs create my-app
|
|
29
|
-
cd my-app
|
|
30
|
-
npm run dev
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Leave `npm run dev` running in a terminal. It rebuilds your server, regenerates the typed client, and hot-reloads the browser every time you save. The app is at `http://localhost:3000`.
|
|
34
|
-
|
|
35
|
-
## Why the database (and not a variable)
|
|
36
|
-
|
|
37
|
-
Your first instinct might be to keep the count in a normal variable on the server. That will not work, and it is worth understanding why now.
|
|
38
|
-
|
|
39
|
-
**The server runs a fresh copy of your `.wasm` for every request, and wipes its memory when the request ends.** So a variable you set while handling one request is gone by the next request. Anything that must outlive a single request, a counter, a user, a post, has to go into a store. toiljs ships one: **ToilDB**, a database built into the edge.
|
|
40
|
-
|
|
41
|
-
ToilDB offers a few specialized shapes called **families**. For a running total, the right one is a **counter**: a value you can atomically add to and read back. (Others include documents, events, and views. See [Database overview](../database/README.md).)
|
|
42
|
-
|
|
43
|
-
## Step 1: add a data type for the response
|
|
44
|
-
|
|
45
|
-
Your server and client talk in typed messages called `@data` classes. A `@data` class can cross the wire and be parsed into a real typed object on the other side. Create one to hold the count.
|
|
46
|
-
|
|
47
|
-
Create `server/models/LikeCount.ts`:
|
|
48
|
-
|
|
49
|
-
```ts
|
|
50
|
-
// The response our route returns: just the current like count.
|
|
51
|
-
@data
|
|
52
|
-
export class LikeCount {
|
|
53
|
-
count: i64 = 0;
|
|
54
|
-
constructor(count: i64 = 0) {
|
|
55
|
-
this.count = count;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Two things to notice:
|
|
61
|
-
|
|
62
|
-
- `@data` is a decorator that marks this class as a wire type. You use it with no import (it is a compiler built-in). See [Data types](../backend/data.md).
|
|
63
|
-
- The field type is `i64`, a 64-bit integer. The server uses precise integer types like `i64` and `u64`. On the client side these arrive as JavaScript `bigint`. More on this in [Types](../concepts/types.md).
|
|
64
|
-
|
|
65
|
-
## Step 2: add the HTTP route
|
|
66
|
-
|
|
67
|
-
Now the backend endpoint. Create `server/routes/Likes.ts`:
|
|
68
|
-
|
|
69
|
-
```ts
|
|
70
|
-
import { RouteContext } from 'toiljs/server/runtime';
|
|
71
|
-
|
|
72
|
-
import { LikeCount } from '../models/LikeCount';
|
|
73
|
-
|
|
74
|
-
// The KEY that names one counter. A counter is a map from a key to a number,
|
|
75
|
-
// so a single fixed key ("home") gives us one global tally.
|
|
76
|
-
@data
|
|
77
|
-
class LikeKey {
|
|
78
|
-
page: string = 'home';
|
|
79
|
-
constructor(page: string = 'home') {
|
|
80
|
-
this.page = page;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// A @database declares your ToilDB collections. Each @collection is one named
|
|
85
|
-
// store. Here we declare a single counter keyed by LikeKey.
|
|
86
|
-
@database
|
|
87
|
-
class LikesDb {
|
|
88
|
-
@collection static likes: Counter<LikeKey>;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// A @rest controller exposes HTTP endpoints. 'likes' is the URL prefix, so the
|
|
92
|
-
// routes below live under /likes.
|
|
93
|
-
@rest('likes')
|
|
94
|
-
class Likes {
|
|
95
|
-
// GET /likes -> read the current count.
|
|
96
|
-
// A @get handler is a "query": it may read but not write. Reading one
|
|
97
|
-
// counter by its key is a point read, which queries are allowed to do.
|
|
98
|
-
@get('/')
|
|
99
|
-
public show(): LikeCount {
|
|
100
|
-
const key = new LikeKey('home');
|
|
101
|
-
return new LikeCount(LikesDb.likes.get(key));
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// POST /likes -> add one, then return the new count.
|
|
105
|
-
// A @post handler is an "action": it may write. `add` bumps the counter.
|
|
106
|
-
// (A body-less POST takes the RouteContext; we do not need it here.)
|
|
107
|
-
@post('/')
|
|
108
|
-
public add(_ctx: RouteContext): LikeCount {
|
|
109
|
-
const key = new LikeKey('home');
|
|
110
|
-
LikesDb.likes.add(key, 1);
|
|
111
|
-
return new LikeCount(LikesDb.likes.get(key));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
What each decorator does:
|
|
117
|
-
|
|
118
|
-
- **`@rest('likes')`** turns the class into an HTTP controller mounted at `/likes`.
|
|
119
|
-
- **`@get('/')`** and **`@post('/')`** map a method to a verb and path. A `@get` is a read-only **query**; a `@post` is a write-capable **action**. This split is how toiljs keeps expensive or unsafe operations out of read paths.
|
|
120
|
-
- **`@database`** and **`@collection`** declare your ToilDB stores. `Counter<LikeKey>` is a counter you look up by a `LikeKey`.
|
|
121
|
-
|
|
122
|
-
The counter gives you two operations: `add(key, delta)` to change it and `get(key)` to read it. See [Counters](../database/counters.md) and [HTTP routes](../backend/rest.md) for the full API.
|
|
123
|
-
|
|
124
|
-
## Step 3: register the route
|
|
125
|
-
|
|
126
|
-
Open `server/main.ts` and add an import for your new route, next to the others:
|
|
127
|
-
|
|
128
|
-
```ts
|
|
129
|
-
import './routes/Likes';
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
The build actually discovers decorated files under `server/` on its own, but importing them from `main.ts` is the convention: it keeps a direct `toilscript` build finding the same code. (Every `app` project already imports its demo routes this way.)
|
|
133
|
-
|
|
134
|
-
Save. Watch the terminal: `toiljs dev` recompiles the server to wasm and regenerates `shared/server.ts`. A moment later your new endpoint exists, fully typed.
|
|
135
|
-
|
|
136
|
-
## Step 4: add the client page
|
|
137
|
-
|
|
138
|
-
Now the frontend. Create `client/routes/likes.tsx`. The file name is the URL, so this page is at `/likes`.
|
|
139
|
-
|
|
140
|
-
```tsx
|
|
141
|
-
import { useEffect, useState } from 'react';
|
|
142
|
-
|
|
143
|
-
export default function LikesPage() {
|
|
144
|
-
// The count is a bigint because the server field is i64.
|
|
145
|
-
const [count, setCount] = useState(0n);
|
|
146
|
-
|
|
147
|
-
// Read the current count once, when the page first loads.
|
|
148
|
-
useEffect(() => {
|
|
149
|
-
Server.REST.likes.show().then((res) => setCount(res.count));
|
|
150
|
-
}, []);
|
|
151
|
-
|
|
152
|
-
// Add a like, then update the UI with the fresh count the server returns.
|
|
153
|
-
const like = async () => {
|
|
154
|
-
const res = await Server.REST.likes.add();
|
|
155
|
-
setCount(res.count);
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
return (
|
|
159
|
-
<main>
|
|
160
|
-
<h1>{String(count)} likes</h1>
|
|
161
|
-
<button onClick={like}>Like</button>
|
|
162
|
-
</main>
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
The magic here is `Server.REST.likes`. You never wrote it. toiljs generated it into `shared/server.ts` from your `@rest` controller, so:
|
|
168
|
-
|
|
169
|
-
- `Server.REST.likes.show()` returns a `Promise<LikeCount>`, and `res.count` is typed for you.
|
|
170
|
-
- `Server.REST.likes.add()` sends the POST and returns the updated `LikeCount`.
|
|
171
|
-
- `Server` is a global in client code, so you do not import it. Its types come from the generated file.
|
|
172
|
-
|
|
173
|
-
If you rename the route or change its return type on the server, this client code stops type-checking until you fix it. That is the whole point: the browser and the backend cannot silently disagree.
|
|
174
|
-
|
|
175
|
-
## Step 5: try it
|
|
176
|
-
|
|
177
|
-
Open `http://localhost:3000/likes`. You should see "0 likes" and a button. Click **Like** a few times and the number climbs.
|
|
178
|
-
|
|
179
|
-
Now the important test: **reload the page.** The count is still there. Stop the dev server, start it again, reload: still there. That is ToilDB persisting your counter, exactly as it would on the real edge. The same code that ran against the local dev database runs against the worldwide database in production, with no connection string to configure.
|
|
180
|
-
|
|
181
|
-
## What just happened
|
|
182
|
-
|
|
183
|
-
```mermaid
|
|
184
|
-
flowchart LR
|
|
185
|
-
M["server/models/LikeCount.ts<br/>@data"] --> GEN
|
|
186
|
-
R["server/routes/Likes.ts<br/>@rest + @database"] --> GEN
|
|
187
|
-
GEN["toiljs dev<br/>compiles + generates"] --> WASM["release.wasm"]
|
|
188
|
-
GEN --> SS["shared/server.ts<br/>(typed Server.REST.likes)"]
|
|
189
|
-
SS --> P["client/routes/likes.tsx<br/>calls Server.REST.likes"]
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
You wrote three small files. toiljs compiled the server to WebAssembly, generated a typed client from your route, and your React page called it with full type safety. The like count lives in ToilDB, so it persists.
|
|
193
|
-
|
|
194
|
-
## Gotchas and notes
|
|
195
|
-
|
|
196
|
-
- **`res.count` is a `bigint`, not a `number`.** That is why the page uses `useState(0n)` and `String(count)`. Server integer types map to `bigint` on the client. See [Types](../concepts/types.md).
|
|
197
|
-
- **A `@get` cannot write, and a `@post` can.** If you try to call `.add(...)` from a `@get`, the compiler rejects it. Reads that scan many rows are also blocked in handlers; a single-key `get` is fine.
|
|
198
|
-
- **You do not edit `shared/server.ts`.** It is regenerated on every server build. Change the route, and the client updates itself.
|
|
199
|
-
- **If the editor does not know `Server.REST.likes` yet**, it is because the server has not rebuilt since you added the route. Save a server file (or restart `toiljs dev`) to regenerate `shared/server.ts`.
|
|
200
|
-
- **The counter is global.** Everyone hitting `/likes` shares the same "home" tally, because we used one fixed key. Use a different key per page or per user to split the count.
|
|
201
|
-
|
|
202
|
-
## Where to go next
|
|
203
|
-
|
|
204
|
-
- Return richer objects and lists: [Data types](../backend/data.md) and [HTTP routes](../backend/rest.md).
|
|
205
|
-
- Call the server without URLs, as plain function calls: [Typed RPC](../backend/rpc.md).
|
|
206
|
-
- Store more than a number: [Documents](../database/documents.md), [Events](../database/events.md), and [Views](../database/views.md).
|
|
207
|
-
- Add login and sessions: [Auth](../auth/README.md).
|
|
208
|
-
- Style your pages: [Styling](../frontend/styling.md) and [Routing](../frontend/routing.md).
|
|
209
|
-
|
|
210
|
-
## Related
|
|
211
|
-
|
|
212
|
-
- [Project structure](./project-structure.md)
|
|
213
|
-
- [Database overview](../database/README.md)
|
|
214
|
-
- [Counters](../database/counters.md)
|
|
215
|
-
- [Backend overview](../backend/README.md)
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# Installation
|
|
2
|
-
|
|
3
|
-
Install the toiljs command-line tool (CLI) so you can create and run projects. This takes a couple of minutes.
|
|
4
|
-
|
|
5
|
-
## Why and when
|
|
6
|
-
|
|
7
|
-
You need the toiljs CLI once, before you create your first project. The CLI is the single tool you use to scaffold, run, build, and check toiljs apps. After a project exists, the same CLI is also installed inside that project, so day-to-day you can run it through your package scripts (`npm run dev`) without a global install.
|
|
8
|
-
|
|
9
|
-
## Prerequisites
|
|
10
|
-
|
|
11
|
-
You need **Node.js version 24.0.0 or newer**. Node.js is the JavaScript runtime that powers the toiljs CLI, the dev server, and the build. (Your backend does not run on Node.js, but the tools that build it do.)
|
|
12
|
-
|
|
13
|
-
Check your version:
|
|
14
|
-
|
|
15
|
-
```sh
|
|
16
|
-
node --version
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
If it prints `v24.0.0` or higher, you are set. If it is older or the command is not found, install a current Node.js from [nodejs.org](https://nodejs.org), or use a version manager like [nvm](https://github.com/nvm-sh/nvm) or [fnm](https://github.com/Schniz/fnm):
|
|
20
|
-
|
|
21
|
-
```sh
|
|
22
|
-
# with nvm
|
|
23
|
-
nvm install 24
|
|
24
|
-
nvm use 24
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
You also need a package manager. **npm** comes with Node.js, so you already have it. toiljs also supports **pnpm**, **yarn**, and **bun** if you prefer one of those.
|
|
28
|
-
|
|
29
|
-
### TypeScript 6, not 7
|
|
30
|
-
|
|
31
|
-
toiljs requires **TypeScript 6** (`>=6.0.0 <7.0.0`). TypeScript 7 is not supported yet.
|
|
32
|
-
|
|
33
|
-
TypeScript 7 is the native (Go) port of the compiler. It ships a much faster `tsc`, but its package no longer exports the JavaScript compiler API, whose main entry is now just `{ version, versionMajorMinor }`. toiljs reads each route's static `metadata` export through that API to bake your SEO tags into the built HTML, and the `toiljs/eslint` preset loads it too. On TypeScript 7 the metadata baking silently stops (your built pages lose their tags) and typescript-eslint fails to load at all.
|
|
34
|
-
|
|
35
|
-
Pin TypeScript in your `package.json`:
|
|
36
|
-
|
|
37
|
-
```json
|
|
38
|
-
{
|
|
39
|
-
"devDependencies": {
|
|
40
|
-
"typescript": "^6.0.3"
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
`toiljs doctor` flags an unsupported TypeScript, and `toiljs doctor --fix` pins it back for you. `toiljs update` will not upgrade you into TypeScript 7. Support will land once the tools toiljs builds on can read the new `typescript/unstable/*` API.
|
|
46
|
-
|
|
47
|
-
## Install the CLI
|
|
48
|
-
|
|
49
|
-
The package is called `toiljs`, and it provides a command named `toiljs`. You have two ways to use it.
|
|
50
|
-
|
|
51
|
-
### Option A: run it on demand with npx (no install)
|
|
52
|
-
|
|
53
|
-
`npx` comes with npm and runs a package without installing it globally. This is the quickest way to create your first project:
|
|
54
|
-
|
|
55
|
-
```sh
|
|
56
|
-
npx toiljs create my-app
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Option B: install it globally
|
|
60
|
-
|
|
61
|
-
If you plan to create projects often, install it once so `toiljs` is always on your path:
|
|
62
|
-
|
|
63
|
-
```sh
|
|
64
|
-
npm install -g toiljs
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Then you can run `toiljs` directly:
|
|
68
|
-
|
|
69
|
-
```sh
|
|
70
|
-
toiljs create my-app
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Both options end up at the same place. The rest of these docs write `toiljs <command>`; if you did not install globally, just put `npx` in front (`npx toiljs <command>`).
|
|
74
|
-
|
|
75
|
-
## Verify it works
|
|
76
|
-
|
|
77
|
-
Check the version:
|
|
78
|
-
|
|
79
|
-
```sh
|
|
80
|
-
toiljs --version
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
You should see a version number printed (for example, `0.0.86`).
|
|
84
|
-
|
|
85
|
-
To see every command and flag, run help:
|
|
86
|
-
|
|
87
|
-
```sh
|
|
88
|
-
toiljs --help
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Inside a project, there is a deeper health check called **doctor**. It inspects your setup and dependencies and tells you exactly what to fix. You will use it after you create a project, but it is good to know it exists:
|
|
92
|
-
|
|
93
|
-
```sh
|
|
94
|
-
toiljs doctor
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
`toiljs doctor` reads your project (its `package.json`, config, routes, and build output), runs a series of checks, and prints a grouped report. One of the first checks is your Node.js version against the required `>=24.0.0`, so if your Node is too old, doctor will say so. Add `--fix` to let it repair the things it can (such as the typed-RPC wiring), or `--json` for machine-readable output in a CI pipeline.
|
|
98
|
-
|
|
99
|
-
## How the tooling fits together
|
|
100
|
-
|
|
101
|
-
The `toiljs` CLI is a friendly front end over two underlying tools. You rarely call them directly, but it helps to know they exist.
|
|
102
|
-
|
|
103
|
-
```mermaid
|
|
104
|
-
flowchart LR
|
|
105
|
-
CLI["toiljs CLI"] --> TS["toilscript<br/>(compiles server/ to wasm)"]
|
|
106
|
-
CLI --> VITE["Vite<br/>(bundles client/ React)"]
|
|
107
|
-
TS --> W["build/server/release.wasm"]
|
|
108
|
-
VITE --> B["build/client/"]
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
When you create a project, both `toiljs` and `toilscript` are added to it as dependencies, so everything is pinned to versions that work together. You do not install `toilscript` separately.
|
|
112
|
-
|
|
113
|
-
## Gotchas and notes
|
|
114
|
-
|
|
115
|
-
- **"command not found: toiljs"** after a global install usually means npm's global bin folder is not on your `PATH`. Either fix your `PATH` or just use `npx toiljs ...` instead.
|
|
116
|
-
- **Node too old** is the most common first-time failure. WebAssembly tooling and the build depend on features in Node 24 and up. Upgrade before creating a project.
|
|
117
|
-
- **Every command checks for updates.** On each run, the CLI quietly checks npm for a newer toiljs and prints a note if you are behind. It never blocks the command. To turn it off, set the environment variable `TOILJS_NO_UPDATE_CHECK=1`.
|
|
118
|
-
- You do **not** need to install a database, a Docker container, or any cloud account to develop locally. The dev server includes a local ToilDB so your data-backed features run out of the box.
|
|
119
|
-
|
|
120
|
-
## Related
|
|
121
|
-
|
|
122
|
-
- [Create a project](./create-project.md)
|
|
123
|
-
- [The CLI reference](../cli/README.md)
|
|
124
|
-
- [Getting started overview](./README.md)
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
# Migrating an existing React app
|
|
2
|
-
|
|
3
|
-
How to bring a React app you already have into toiljs. The frontend usually moves over with small changes. The backend is the real work, because a toiljs server is not Node.js, and this page is honest about what that means.
|
|
4
|
-
|
|
5
|
-
## Why and when
|
|
6
|
-
|
|
7
|
-
Move to toiljs when you want your frontend and backend in one typed repo, deployed to the edge, with a built-in global database, and you are willing to rewrite your server logic against toiljs's rules. If you only want a React bundler, toiljs is more than you need. If you want the full-stack, typed, edge model, this is the payoff.
|
|
8
|
-
|
|
9
|
-
The safest approach is **not** to convert your old project in place. Instead, scaffold a fresh toiljs project and move code into it piece by piece. A fresh project comes with the required presets, config, and routing already wired, so you spend your time on your code, not on plumbing.
|
|
10
|
-
|
|
11
|
-
```sh
|
|
12
|
-
toiljs create my-app
|
|
13
|
-
cd my-app
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## The big picture: what moves where
|
|
17
|
-
|
|
18
|
-
```mermaid
|
|
19
|
-
flowchart LR
|
|
20
|
-
subgraph OLD["Your current app"]
|
|
21
|
-
FE["React components,<br/>pages, styles, assets"]
|
|
22
|
-
BE["Node/Express backend,<br/>DB access, npm libs"]
|
|
23
|
-
end
|
|
24
|
-
FE -->|"mostly copy"| C["toiljs client/"]
|
|
25
|
-
BE -->|"rewrite"| S["toiljs server/"]
|
|
26
|
-
C -.->|"generated typed client"| S
|
|
27
|
-
S --> DB[("ToilDB")]
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
- Your **React frontend** copies into `client/` with modest changes (routing and asset paths).
|
|
31
|
-
- Your **backend** is **rewritten** into `server/` as toilscript, because it compiles to WebAssembly, not Node.
|
|
32
|
-
|
|
33
|
-
## The frontend: what changes
|
|
34
|
-
|
|
35
|
-
`client/` is a normal Vite + React app, so most of your frontend works as is. The common adjustments:
|
|
36
|
-
|
|
37
|
-
- **Routing becomes file-based.** toiljs has no `<Routes>`/`<Route>` config. A file at `client/routes/about.tsx` is the `/about` page; `client/routes/blog/[slug].tsx` is `/blog/:slug`. Move each page component to the matching file and delete your router setup. Use `Toil.Link` for navigation instead of your router's `Link`. See [Routing](../frontend/routing.md).
|
|
38
|
-
- **Static assets move to `client/public/`.** Files there are served as is (images at `/images/...`, plus `favicon` and `robots.txt`).
|
|
39
|
-
- **Global styles move to `client/styles/`**, and are imported from `client/toil.tsx`. See [Styling](../frontend/styling.md).
|
|
40
|
-
- **Your React npm packages are fine.** The client is regular JavaScript, so component libraries, state managers, and browser APIs all work.
|
|
41
|
-
- **Data fetching changes** if you want the typed client: replace hand-written `fetch('/api/...')` calls with the generated `Server.REST.*` methods. You can keep raw `fetch` too, but you lose the type safety. See [Fetching data](../frontend/data-fetching.md).
|
|
42
|
-
|
|
43
|
-
Metadata and SEO that you used a helmet library for is built in: set a `metadata` export per route, or use `Toil.Head`. See [Metadata and SEO](../frontend/metadata.md).
|
|
44
|
-
|
|
45
|
-
## The backend: the honest part
|
|
46
|
-
|
|
47
|
-
This is where migration takes real effort. Read this section carefully before you plan the work.
|
|
48
|
-
|
|
49
|
-
Your toiljs server is compiled by **toilscript** into WebAssembly. toilscript accepts a **strict subset of TypeScript**. It looks like TypeScript, but treat it as a small, separate language that happens to share the syntax. The practical consequences:
|
|
50
|
-
|
|
51
|
-
### What is NOT available on the server
|
|
52
|
-
|
|
53
|
-
- **No arbitrary npm packages.** You cannot `import` a library from `node_modules` into `server/`. There is no `express`, no `pg`, no `stripe` SDK, no `lodash`. If your route logic leans on npm libraries, that logic has to be reworked.
|
|
54
|
-
- **No Node.js APIs.** No `fs`, `http`, `process`, `Buffer`, `path`, or other Node built-ins. The server runs in a sandbox, not in Node.
|
|
55
|
-
- **No DOM or browser APIs**, because it is not a browser either.
|
|
56
|
-
- **No connecting to your existing database.** There is no connection string and no driver. Your data moves to **ToilDB**, the built-in database.
|
|
57
|
-
|
|
58
|
-
### What you use instead
|
|
59
|
-
|
|
60
|
-
toiljs replaces the common needs with built-in globals and decorators, so you rarely miss the missing libraries:
|
|
61
|
-
|
|
62
|
-
| You used to reach for | On the toiljs server you use |
|
|
63
|
-
| --- | --- |
|
|
64
|
-
| `express` routes / a router | `@rest` controllers with `@get` / `@post` ([HTTP routes](../backend/rest.md)) |
|
|
65
|
-
| A REST client between services | `@service` / `@remote` typed RPC ([RPC](../backend/rpc.md)) |
|
|
66
|
-
| Postgres / Mongo / Redis | [ToilDB](../database/README.md) families (documents, counters, events, views) |
|
|
67
|
-
| `jsonwebtoken`, session middleware | Built-in [auth](../auth/README.md) and cookies |
|
|
68
|
-
| `crypto` from Node | the `crypto` global (synchronous Web Crypto) ([Crypto](../services/crypto.md)) |
|
|
69
|
-
| `nodemailer` / an email SDK | the built-in email service ([Email](../services/email.md)) |
|
|
70
|
-
| `process.env` | `Environment.get()` / `Environment.getSecure()` ([Environment](../services/environment.md)) |
|
|
71
|
-
| `Date.now()`, timers | the `Time` global ([Time](../services/time.md)) |
|
|
72
|
-
|
|
73
|
-
### Type system differences
|
|
74
|
-
|
|
75
|
-
toilscript uses precise, explicit numeric types instead of JavaScript's single `number`. You will write `i32`, `u64`, `f64`, and even `u256`, and be explicit about integer sizes. Values that must cross the wire are `@data` classes with concrete fields, not free-form objects. There is no `any`-style duck typing to lean on. The full rules, and how server types map to `bigint` and friends on the client, are in [Types](../concepts/types.md).
|
|
76
|
-
|
|
77
|
-
### Two behaviors to design around
|
|
78
|
-
|
|
79
|
-
- **Memory resets every request.** Each request gets a fresh `.wasm` instance and its memory is wiped afterward. A module-level variable does not persist. Anything durable goes in ToilDB. (This is the same rule you met in [Your first app](./first-app.md).)
|
|
80
|
-
- **Reads and writes are split.** A `@get` is a read-only query; a `@post` is a write action. Operations that scan unbounded data are not allowed in a request handler; they move to a [`@derive`](../background/derive.md) or a background [daemon](../background/daemons.md).
|
|
81
|
-
|
|
82
|
-
## Configuration
|
|
83
|
-
|
|
84
|
-
Two config files replace the various configs you may have had:
|
|
85
|
-
|
|
86
|
-
- **`toil.config.ts`** is your client and build config: SEO defaults, image optimization, page transitions, and dev-server options. It uses `defineConfig`. See [Configuration](../concepts/config.md).
|
|
87
|
-
- **`toilconfig.json`** is the low-level server (wasm) build config. The scaffold sets sensible defaults, and you usually leave it alone.
|
|
88
|
-
|
|
89
|
-
Environment variables move from a `.env` you read with `process.env` to `.env` / `.env.secrets` files you read with `Environment.get()` / `Environment.getSecure()`. Secrets and plain vars are kept in separate buckets. See [Environment and secrets](../services/environment.md).
|
|
90
|
-
|
|
91
|
-
## Step by step
|
|
92
|
-
|
|
93
|
-
1. **Scaffold a fresh project** with `toiljs create` and get it running with `npm run dev`. Start from something that works.
|
|
94
|
-
2. **Move the frontend.** Copy components into `client/components/`, turn each page into a file under `client/routes/`, move styles into `client/styles/`, and static assets into `client/public/`. Swap your router's `Link` for `Toil.Link`. Install your client-side npm dependencies.
|
|
95
|
-
3. **Get the client rendering** against the routes, even if the data is still stubbed or points at your old backend. Fix routing and asset paths first.
|
|
96
|
-
4. **Rebuild the backend, one route at a time.** For each old endpoint, add a `@data` model in `server/models/`, a `@rest` controller in `server/routes/`, and move its data into a ToilDB family. Import each new route in `server/main.ts`. This is the bulk of the effort; go endpoint by endpoint.
|
|
97
|
-
5. **Move persistence to ToilDB.** Map each table or collection to a ToilDB family (a document store, a counter, an event log, or a view). See [Database overview](../database/README.md) for choosing a family.
|
|
98
|
-
6. **Switch the client to the typed client.** Replace `fetch('/api/...')` calls with `Server.REST.*` (or `Server.<service>.*` for RPC). Now a backend change that breaks the contract shows up as a client type error.
|
|
99
|
-
7. **Run the doctor.** `toiljs doctor` checks your wiring (routes, RPC generation, config, dependencies) and points at anything still off. Add `--fix` to let it repair what it can.
|
|
100
|
-
|
|
101
|
-
## When toiljs is not the right move
|
|
102
|
-
|
|
103
|
-
Be honest with yourself about the backend rewrite. Migration is a poor fit if:
|
|
104
|
-
|
|
105
|
-
- Your server depends heavily on npm libraries or Node-only APIs that have no toiljs equivalent, and rewriting them is not worth it.
|
|
106
|
-
- You must keep talking to an existing external database or service that toiljs cannot reach. (Daemons can make outbound HTTP calls in some cases, but general server-side networking is limited by design.)
|
|
107
|
-
- You need long-lived in-process server state that does not fit the per-request, database-backed model.
|
|
108
|
-
|
|
109
|
-
In those cases, you can still adopt toiljs for the frontend and keep your existing backend, calling it with plain `fetch`. You just give up the single-repo, end-to-end-typed benefits for that part.
|
|
110
|
-
|
|
111
|
-
## Gotchas and notes
|
|
112
|
-
|
|
113
|
-
- **Do not try to `import` server code into the client or vice versa.** They compile with different rules. The only bridge is the generated `shared/server.ts`.
|
|
114
|
-
- **One `@data` type per file** under `models/` matches the tooling's expectations.
|
|
115
|
-
- **`shared/server.ts` is generated**, so it will not exist until your first build, and you never edit it.
|
|
116
|
-
- **Plan the backend as a rewrite, not a port.** The frontend moves; the backend is re-expressed in toiljs's model. Budget for that.
|
|
117
|
-
|
|
118
|
-
## Related
|
|
119
|
-
|
|
120
|
-
- [Getting started overview](./README.md)
|
|
121
|
-
- [Backend overview](../backend/README.md)
|
|
122
|
-
- [Types](../concepts/types.md)
|
|
123
|
-
- [Database overview](../database/README.md)
|
|
124
|
-
- [The CLI reference](../cli/README.md)
|
|
125
|
-
- [Configuration](../concepts/config.md)
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
# Project structure
|
|
2
|
-
|
|
3
|
-
A tour of every folder and file in a toiljs project, what each one is for, and the single most important question: **where does this code run, the browser or the edge?**
|
|
4
|
-
|
|
5
|
-
## Why this matters
|
|
6
|
-
|
|
7
|
-
toiljs blends frontend and backend into one repo, so the same `.ts` file extension can mean two very different things. A file in `client/` becomes JavaScript that runs in your user's browser. A file in `server/` becomes WebAssembly that runs on the edge, with different rules and different available APIs. Knowing which folder you are in tells you what you are allowed to do. Keep this mental split and everything else falls into place.
|
|
8
|
-
|
|
9
|
-
```mermaid
|
|
10
|
-
flowchart TD
|
|
11
|
-
subgraph Browser["Runs in the browser"]
|
|
12
|
-
C["client/*"]
|
|
13
|
-
end
|
|
14
|
-
subgraph Edge["Runs on the edge (WebAssembly)"]
|
|
15
|
-
S["server/* -> release.wasm"]
|
|
16
|
-
end
|
|
17
|
-
subgraph Generated["Generated glue (types + config)"]
|
|
18
|
-
SH["shared/server.ts"]
|
|
19
|
-
CFG["toil.config.ts / toilconfig.json"]
|
|
20
|
-
end
|
|
21
|
-
C -. "typed calls" .-> SH
|
|
22
|
-
SH -. "HTTP / realtime" .-> S
|
|
23
|
-
S --> DB[("ToilDB")]
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## The top level
|
|
27
|
-
|
|
28
|
-
These files sit in your project root.
|
|
29
|
-
|
|
30
|
-
| File | What it is | Runs where |
|
|
31
|
-
| --- | --- | --- |
|
|
32
|
-
| `package.json` | Scripts (`dev`, `build`, `lint`, `typecheck`, `format`) and dependencies (`toiljs`, `react`, `toilscript`, ...) | tooling only |
|
|
33
|
-
| `toil.config.ts` | **Client and build config.** Uses `defineConfig` to set SEO, images, page transitions, and dev options. | tooling only |
|
|
34
|
-
| `toilconfig.json` | **Server (wasm) build config** for toilscript: the entry file, the output `.wasm` path, and low-level compile options. You rarely edit this. | tooling only |
|
|
35
|
-
| `tsconfig.json` | TypeScript config for the client (`client/`, `shared/`, `emails/`). Extends `toiljs/tsconfig`. | tooling only |
|
|
36
|
-
| `eslint.config.js` | Linting preset (`toiljs/eslint`). | tooling only |
|
|
37
|
-
| `.prettierrc` | Formatting preset (`toiljs/prettier`). | tooling only |
|
|
38
|
-
| `.prettierignore` | Files Prettier should skip (generated files). | tooling only |
|
|
39
|
-
| `.gitignore` | Ignores `build/`, `.toil/`, generated files, and your `.env` files. | tooling only |
|
|
40
|
-
| `.vscode/settings.json` | Tells VS Code to use the project's TypeScript so the toilscript editor plugin loads. | editor only |
|
|
41
|
-
| `toil-env.d.ts` | **Generated** editor types for client globals like `Toil.Link` and `Toil.Image`. Do not edit. | editor only |
|
|
42
|
-
| `toil-routes.d.ts` | **Generated** list of your real route names, so `Toil.Link href="..."` type-checks. Filled in on the first build. | editor only |
|
|
43
|
-
| `README.md` | Your project's readme. | docs |
|
|
44
|
-
| `CLAUDE.md`, `AGENTS.md`, etc. | Optional AI-assistant hint files that point tools at the toiljs docs. | docs |
|
|
45
|
-
|
|
46
|
-
Two folders you may also see at the root:
|
|
47
|
-
|
|
48
|
-
- **`.toil/`** is a working directory toiljs manages (a build cache and a copy of the docs). It is gitignored. You never edit it.
|
|
49
|
-
- **`.env` and `.env.secrets`** are files **you** create when you need local environment variables or secrets during `toiljs dev`. They are gitignored so you never commit them, and the edge loads their real values out of band in production. Your server reads them with `Environment.get("KEY")` and `Environment.getSecure("KEY")`. See [Environment and secrets](../services/environment.md).
|
|
50
|
-
|
|
51
|
-
## `client/` (runs in the browser)
|
|
52
|
-
|
|
53
|
-
This is a normal React app. You can use React libraries and browser APIs here freely.
|
|
54
|
-
|
|
55
|
-
```text
|
|
56
|
-
client/
|
|
57
|
-
toil.tsx the entry point; mounts your app
|
|
58
|
-
layout.tsx the root layout wrapping every page
|
|
59
|
-
404.tsx the not-found page
|
|
60
|
-
global-error.tsx the top-level error page
|
|
61
|
-
routes/ file-based pages
|
|
62
|
-
components/ shared React components
|
|
63
|
-
styles/ global stylesheets
|
|
64
|
-
public/ static files served as-is
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
- **`toil.tsx`** is the entry file. It imports your global styles and calls `Toil.mount(...)` to start the app. You rarely change it beyond the style imports.
|
|
68
|
-
- **`layout.tsx`** is your root layout: the header, footer, and page shell that wrap every route. Its `children` prop is the current page.
|
|
69
|
-
- **`404.tsx`** renders when no route matches. **`global-error.tsx`** renders when a route throws.
|
|
70
|
-
- **`routes/`** is where pages live, and the file name **is** the URL. `routes/index.tsx` is `/`, `routes/about.tsx` is `/about`, `routes/blog/[slug].tsx` is `/blog/:slug`. This is called **file-based routing**. See [Routing](../frontend/routing.md).
|
|
71
|
-
- **`components/`** holds React components you reuse across pages. Nothing here is a route.
|
|
72
|
-
- **`styles/`** holds your global CSS (or Sass, Less, or Stylus, if you chose one). See [Styling](../frontend/styling.md).
|
|
73
|
-
- **`public/`** holds static files served exactly as they are: `favicon`, `robots.txt`, and an `images/` folder (reachable at `/images/...`). The `public/index.html` is the base HTML shell your app mounts into.
|
|
74
|
-
|
|
75
|
-
A single global, `Toil`, is available in client code without an import (for `Toil.Link`, `Toil.Image`, and `Toil.Head`). It is typed by the generated `toil-env.d.ts`.
|
|
76
|
-
|
|
77
|
-
## `server/` (runs on the edge, as WebAssembly)
|
|
78
|
-
|
|
79
|
-
This is your backend. It is compiled by toilscript into one `.wasm` file. Remember the two rules: **memory resets every request**, and **this is not Node.js** (a strict TypeScript subset, no arbitrary npm packages). See [Backend overview](../backend/README.md) and [Types](../concepts/types.md).
|
|
80
|
-
|
|
81
|
-
```text
|
|
82
|
-
server/
|
|
83
|
-
main.ts the entry: wires the handler + imports your modules
|
|
84
|
-
tsconfig.json server-only TS config (loads the toilscript editor plugin)
|
|
85
|
-
toil-server-env.d.ts generated editor types for server globals
|
|
86
|
-
core/ your request handler and shared logic
|
|
87
|
-
models/ @data classes
|
|
88
|
-
routes/ @rest controllers (HTTP)
|
|
89
|
-
services/ @service / @remote (typed RPC)
|
|
90
|
-
migrations/ ToilDB schema migrations
|
|
91
|
-
scheduled/ reserved for scheduled tasks
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
- **`main.ts`** is the entry the build compiles. It does three required things: it sets `Server.handler` (a factory that returns one fresh handler per request), it re-exports the wasm entry points (`export * from 'toiljs/server/runtime/exports'`), and it defines the `abort` hook. It also `import`s your other server modules so a direct toilscript run builds the same code.
|
|
95
|
-
- **`core/`** holds your top-level `ToilHandler` (often `AppHandler.ts`): the first code that sees each request. It can dispatch to your `@rest` controllers and then fall through to any hand-written logic.
|
|
96
|
-
- **`models/`** holds your `@data` classes, one type per file. A `@data` class is a typed message that can cross the wire between client and server (and into ToilDB). See [Data types](../backend/data.md).
|
|
97
|
-
- **`routes/`** holds your `@rest` controllers: classes decorated with `@rest`, `@get`, and `@post` that expose HTTP endpoints. See [HTTP routes](../backend/rest.md).
|
|
98
|
-
- **`services/`** holds `@service` classes and free `@remote` functions: typed remote calls the client makes as plain function calls (no URLs). See [Typed RPC](../backend/rpc.md).
|
|
99
|
-
- **`migrations/`** holds ToilDB schema migrations. When you change the shape of a stored `@data` type, you add a `<Type>.migration.ts` here that carries old records forward. The compiler enforces this convention. See [Documents](../database/documents.md).
|
|
100
|
-
- **`scheduled/`** is reserved for scheduled tasks. New decorated files anywhere under `server/` are picked up automatically by the build.
|
|
101
|
-
- **`tsconfig.json`** and **`toil-server-env.d.ts`** are editor-support files. They teach your editor about the server globals (`crypto`, `Cookie`, `Environment`, and friends) so it stops flagging them. They do not affect the build.
|
|
102
|
-
|
|
103
|
-
### How the build discovers your server code
|
|
104
|
-
|
|
105
|
-
You do not register routes in a config file. The compiler scans every `.ts` file under `server/` and picks up anything that declares a decorated surface (`@rest`, `@service`, `@remote`, `@data`, `@user`, `@database`, and so on). Importing those files from `main.ts` is still good practice: it keeps a direct `toilscript` run building the exact same server.
|
|
106
|
-
|
|
107
|
-
## `shared/` (generated glue)
|
|
108
|
-
|
|
109
|
-
```text
|
|
110
|
-
shared/
|
|
111
|
-
server.ts GENERATED typed client (do not edit)
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
**`shared/server.ts` is written for you** by the server build. It contains:
|
|
115
|
-
|
|
116
|
-
- A typed `Server` object the browser uses to call your backend: `Server.REST.*` for HTTP routes and `Server.<service>.*` for RPC.
|
|
117
|
-
- The client-side codecs for every `@data` class, so responses come back as real typed objects.
|
|
118
|
-
- A `getUser()` helper for reading the signed-in user on the client.
|
|
119
|
-
|
|
120
|
-
Because it is generated, it does not exist in a fresh project and it is gitignored. It appears the first time you run `toiljs dev` or `toiljs build`. Never hand-edit it; change your server code and it regenerates.
|
|
121
|
-
|
|
122
|
-
## `build/` (compiled output)
|
|
123
|
-
|
|
124
|
-
```text
|
|
125
|
-
build/
|
|
126
|
-
server/release.wasm your compiled backend (+ release.wat, a readable text form)
|
|
127
|
-
client/ the bundled React app (from Vite)
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
This is what actually ships. It is gitignored and recreated by `toiljs build`. You do not edit anything here.
|
|
131
|
-
|
|
132
|
-
## Putting it together: one request
|
|
133
|
-
|
|
134
|
-
```mermaid
|
|
135
|
-
sequenceDiagram
|
|
136
|
-
participant B as Browser (client/)
|
|
137
|
-
participant SH as shared/server.ts
|
|
138
|
-
participant W as server.wasm (server/)
|
|
139
|
-
participant DB as ToilDB
|
|
140
|
-
B->>SH: Server.REST.likes.like()
|
|
141
|
-
SH->>W: HTTP POST /likes
|
|
142
|
-
W->>DB: counter.add(key, 1)
|
|
143
|
-
W-->>SH: typed LikeCount response
|
|
144
|
-
SH-->>B: { count }
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
The browser calls a typed method, the generated client turns it into an HTTP request, your `.wasm` handles it and touches ToilDB, and a typed result comes back. You wrote both ends; the middle is generated.
|
|
148
|
-
|
|
149
|
-
## Gotchas and notes
|
|
150
|
-
|
|
151
|
-
- **A `.ts` file's folder decides its rules.** The same code that works in `client/` may not compile in `server/`, because the server is a strict subset without Node APIs.
|
|
152
|
-
- **Do not edit generated files.** `shared/server.ts`, `toil-env.d.ts`, `toil-routes.d.ts`, and `toil-server-env.d.ts` are all regenerated by the build and will overwrite your changes.
|
|
153
|
-
- **One `@data` type per file** under `models/` keeps things tidy and matches the convention the tooling expects.
|
|
154
|
-
- **`build/` and `.toil/` are disposable.** Delete them and the next build recreates them.
|
|
155
|
-
|
|
156
|
-
## Related
|
|
157
|
-
|
|
158
|
-
- [Your first app](./first-app.md)
|
|
159
|
-
- [Frontend overview](../frontend/README.md) and [Routing](../frontend/routing.md)
|
|
160
|
-
- [Backend overview](../backend/README.md)
|
|
161
|
-
- [Database overview](../database/README.md)
|
|
162
|
-
- [Configuration](../concepts/config.md)
|
|
163
|
-
- [Decorators reference](../concepts/decorators.md)
|