toiljs 0.0.85 → 0.0.87

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 (132) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +2 -2
  3. package/build/cli/.tsbuildinfo +1 -1
  4. package/build/cli/index.js +303 -293
  5. package/build/compiler/.tsbuildinfo +1 -1
  6. package/build/compiler/config.d.ts +2 -0
  7. package/build/compiler/config.js +1 -0
  8. package/build/compiler/docs.js +8 -24
  9. package/build/compiler/generate.js +4 -2
  10. package/build/compiler/index.d.ts +1 -1
  11. package/build/compiler/index.js +69 -6
  12. package/build/compiler/toil-docs.generated.js +64 -22
  13. package/build/devserver/.tsbuildinfo +1 -1
  14. package/build/devserver/analytics/index.js +7 -3
  15. package/build/devserver/db/database.d.ts +4 -0
  16. package/build/devserver/db/database.js +43 -1
  17. package/build/devserver/db/index.d.ts +1 -1
  18. package/build/devserver/db/index.js +1 -1
  19. package/build/devserver/db/types.d.ts +3 -0
  20. package/build/devserver/db/types.js +2 -0
  21. package/build/devserver/runtime/module.js +4 -1
  22. package/docs/README.md +104 -65
  23. package/docs/auth/README.md +102 -0
  24. package/docs/auth/configuration.md +94 -0
  25. package/docs/auth/extending.md +202 -0
  26. package/docs/auth/how-it-works.md +138 -0
  27. package/docs/auth/usage.md +188 -0
  28. package/docs/backend/README.md +143 -0
  29. package/docs/backend/data.md +351 -0
  30. package/docs/backend/rest.md +402 -0
  31. package/docs/backend/rpc.md +226 -0
  32. package/docs/background/README.md +114 -0
  33. package/docs/background/daemons.md +230 -0
  34. package/docs/background/derive.md +179 -0
  35. package/docs/cli/README.md +377 -0
  36. package/docs/concepts/config.md +416 -0
  37. package/docs/concepts/decorators.md +127 -0
  38. package/docs/concepts/security.md +108 -0
  39. package/docs/concepts/tiers.md +166 -0
  40. package/docs/concepts/types.md +216 -0
  41. package/docs/database/README.md +143 -0
  42. package/docs/database/capacity.md +350 -0
  43. package/docs/database/counters.md +174 -0
  44. package/docs/database/documents.md +255 -0
  45. package/docs/database/events.md +307 -0
  46. package/docs/database/membership.md +246 -0
  47. package/docs/database/setup.md +155 -0
  48. package/docs/database/unique.md +216 -0
  49. package/docs/database/views.md +246 -0
  50. package/docs/frontend/README.md +101 -0
  51. package/docs/frontend/data-fetching.md +243 -0
  52. package/docs/frontend/images.md +148 -0
  53. package/docs/frontend/metadata.md +344 -0
  54. package/docs/frontend/rendering.md +236 -0
  55. package/docs/frontend/routing.md +344 -0
  56. package/docs/frontend/scripts.md +118 -0
  57. package/docs/frontend/search.md +191 -0
  58. package/docs/frontend/styling.md +147 -0
  59. package/docs/getting-started/README.md +81 -0
  60. package/docs/getting-started/create-project.md +131 -0
  61. package/docs/getting-started/deploy.md +101 -0
  62. package/docs/getting-started/first-app.md +215 -0
  63. package/docs/getting-started/installation.md +106 -0
  64. package/docs/getting-started/migrating.md +125 -0
  65. package/docs/getting-started/project-structure.md +163 -0
  66. package/docs/introduction/README.md +41 -0
  67. package/docs/introduction/design-principles.md +55 -0
  68. package/docs/introduction/distributed.md +74 -0
  69. package/docs/introduction/how-it-works.md +74 -0
  70. package/docs/introduction/hyperscale.md +51 -0
  71. package/docs/introduction/modern-stack.md +36 -0
  72. package/docs/introduction/vs-other-frameworks.md +48 -0
  73. package/docs/introduction/why-toil.md +93 -0
  74. package/docs/llms.txt +90 -0
  75. package/docs/realtime/README.md +102 -0
  76. package/docs/realtime/channels.md +211 -0
  77. package/docs/realtime/streams.md +369 -0
  78. package/docs/services/README.md +60 -0
  79. package/docs/services/analytics.md +268 -0
  80. package/docs/services/caching.md +175 -0
  81. package/docs/services/cookies.md +235 -0
  82. package/docs/services/crypto.md +209 -0
  83. package/docs/services/email.md +289 -0
  84. package/docs/services/environment.md +141 -0
  85. package/docs/services/ratelimit.md +174 -0
  86. package/docs/services/time.md +85 -0
  87. package/examples/basic/client/routes/analytics.tsx +13 -12
  88. package/examples/basic/server/models/SiteAnalytics.ts +29 -17
  89. package/examples/basic/server/routes/Analytics.ts +15 -17
  90. package/examples/basic/server/routes/UserId.ts +22 -0
  91. package/package.json +15 -11
  92. package/scripts/gen-toil-docs.mjs +24 -35
  93. package/server/auth/AuthController.ts +336 -0
  94. package/server/auth/AuthUser.ts +23 -0
  95. package/server/auth/index.ts +16 -0
  96. package/server/globals/auth.ts +31 -0
  97. package/server/globals/userid.ts +107 -0
  98. package/src/compiler/config.ts +13 -0
  99. package/src/compiler/docs.ts +16 -33
  100. package/src/compiler/generate.ts +6 -2
  101. package/src/compiler/index.ts +114 -6
  102. package/src/compiler/toil-docs.generated.ts +64 -22
  103. package/src/devserver/analytics/index.ts +10 -4
  104. package/src/devserver/db/database.ts +67 -1
  105. package/src/devserver/db/index.ts +1 -0
  106. package/src/devserver/db/types.ts +13 -0
  107. package/src/devserver/runtime/module.ts +7 -0
  108. package/test/analytics-dev.test.ts +2 -1
  109. package/test/devserver-database.test.ts +113 -0
  110. package/docs/auth-todo.md +0 -149
  111. package/docs/auth.md +0 -322
  112. package/docs/caching.md +0 -115
  113. package/docs/cli.md +0 -17
  114. package/docs/client.md +0 -39
  115. package/docs/cookies.md +0 -457
  116. package/docs/crypto.md +0 -130
  117. package/docs/daemon.md +0 -123
  118. package/docs/data.md +0 -131
  119. package/docs/derive.md +0 -159
  120. package/docs/email.md +0 -326
  121. package/docs/environment.md +0 -97
  122. package/docs/getting-started.md +0 -128
  123. package/docs/index.md +0 -30
  124. package/docs/ratelimit.md +0 -95
  125. package/docs/routing.md +0 -259
  126. package/docs/rpc.md +0 -149
  127. package/docs/server.md +0 -61
  128. package/docs/ssr.md +0 -632
  129. package/docs/streams.md +0 -178
  130. package/docs/styling.md +0 -22
  131. package/docs/tiers.md +0 -133
  132. package/docs/time.md +0 -43
package/docs/routing.md DELETED
@@ -1,259 +0,0 @@
1
- # Routing
2
-
3
- toiljs routing is decorator-driven. You write a controller class, annotate it
4
- with `@rest` and its methods with verb decorators, and the ToilScript compiler
5
- generates the dispatcher. Routes can take a typed body, read path params and the
6
- raw request through a `RouteContext`, and return either a `Response` or a typed
7
- value that is auto-encoded.
8
-
9
- ```ts
10
- import { Response, RouteContext } from 'toiljs/server/runtime';
11
-
12
- @rest('players')
13
- class Players {
14
- @get('/:id')
15
- public get(ctx: RouteContext): Response {
16
- const id = ctx.param('id');
17
- return Response.json(`{"id":"${id}"}`);
18
- }
19
-
20
- @post('/')
21
- public create(input: NewPlayer): Player {
22
- // `input` is the decoded request body; returning a @data value JSON-encodes it
23
- return Player.from(input);
24
- }
25
- }
26
- ```
27
-
28
- ## `@rest` controllers
29
-
30
- `@rest` marks a class as a route controller and mounts it at a prefix.
31
-
32
- ```ts
33
- @rest('api') // mounted at /api
34
- @rest('/') // or @rest('') // mounted at the root
35
- @rest({ stream: DataStream.Binary }) // root mount, binary codec by default
36
- ```
37
-
38
- - The string argument is the mount prefix. `"api"`, `"/api"`, and `"api/"` all
39
- normalize to `/api`; `""` and `"/"` mean the root.
40
- - The object form sets class-wide defaults. `stream: DataStream.Binary` makes
41
- every route in the class use the binary `@data` codec; the default is
42
- `DataStream.JSON`. Individual routes override this with `@route`.
43
-
44
- The compiler injects, at module init, a registration that adds the controller to
45
- the global `Rest` registry. Controllers dispatch in the order their modules are
46
- loaded; routes within a controller try in declaration order, first match wins.
47
-
48
- ## Verb decorators
49
-
50
- Each HTTP method has a decorator taking a single path string:
51
-
52
- ```ts
53
- @get('/path') @post('/path') @put('/path') @delete('/path')
54
- @patch('/path') @head('/path') @options('/path')
55
- ```
56
-
57
- The full path is the controller prefix joined with the route path
58
- (`prefix="/api"`, `@get("/todos/:id")` → `/api/todos/:id`).
59
-
60
- ### `@route` (explicit form)
61
-
62
- `@route` is the general form; use it when you need to set the stream mode per
63
- route or prefer an object:
64
-
65
- ```ts
66
- @route({ method: Methods.POST, path: '/upload', stream: DataStream.Binary })
67
- public upload(body: FileData): FileResult { /* ... */ }
68
- ```
69
-
70
- `method` (from the `Methods` enum) and `path` are required; `stream` is
71
- optional and overrides the controller default.
72
-
73
- ## Path parameters
74
-
75
- A `:name` segment captures that URL segment. Read it with `ctx.param("name")`:
76
-
77
- ```ts
78
- @get('/todos/:id/items/:itemId')
79
- public getItem(ctx: RouteContext): Response {
80
- const id = ctx.param('id');
81
- const itemId = ctx.param('itemId');
82
- return Response.json(`{"todo":"${id}","item":"${itemId}"}`);
83
- }
84
- ```
85
-
86
- Matching is segment-exact: the request path must have the same number of
87
- segments, static segments must match literally, and `:param` segments capture
88
- the value. The query string is stripped before matching.
89
-
90
- ## Method parameters
91
-
92
- A route method takes zero, one, or two parameters, classified by type:
93
-
94
- - a `RouteContext` parameter receives the match context (path params, query,
95
- headers, raw body);
96
- - any other type is treated as the **request body**, decoded as a `@data` value.
97
-
98
- ```ts
99
- @get('/status')
100
- public status(): StatusResponse { /* no body, no context */ }
101
-
102
- @get('/user/:id')
103
- public getUser(ctx: RouteContext): User { /* context only */ }
104
-
105
- @post('/create')
106
- public create(input: NewTodo): Todo { /* body only */ }
107
-
108
- @post('/user/:id/score')
109
- public addScore(input: ScoreDelta, ctx: RouteContext): Player {
110
- const id = ctx.param('id'); /* body AND context */
111
- }
112
- ```
113
-
114
- The body is decoded per the route's stream mode: in JSON mode from
115
- `JSON.parse(ctx.text())`, in Binary mode from `Body.decode(req.body)`. See
116
- [Data codec](./data.md).
117
-
118
- ## Return types
119
-
120
- The compiler encodes the return value by its type:
121
-
122
- | Return type | Result |
123
- | --- | --- |
124
- | `Response` | Returned as-is. Full control over status, headers, body. |
125
- | `void` | `204 No Content`. |
126
- | a `@data` type, JSON stream | `Response.json(value.toJSON().toString())`. |
127
- | a `@data` type, Binary stream | `Response.bytes(value.encode())`. |
128
-
129
- Returning a `Response` lets you set status, headers, cookies, and caching
130
- directly; returning a typed value is the terse path when you just want the data
131
- serialized.
132
-
133
- ## Data streams
134
-
135
- Each route is either **JSON** (default) or **Binary**:
136
-
137
- - **JSON**, the body is `JSON.parse`d and revived via the `@data` type's
138
- `fromJSON`; the response is the type's `toJSON()`. 64-bit-and-larger integers
139
- cross the wire as decimal strings (exact at any size). Best for endpoints a
140
- browser or third party calls directly.
141
- - **Binary**, the body is `Body.decode(bytes)` and the response is
142
- `value.encode()`, using the deterministic `DataWriter`/`DataReader` codec. No
143
- precision loss, smaller, faster. Best for app-to-app and anything
144
- security-sensitive.
145
-
146
- Set the mode on the controller (`@rest({ stream: DataStream.Binary })`) or per
147
- route (`@route({ ..., stream: DataStream.Binary })`).
148
-
149
- ## Dispatch and the 404 fallback
150
-
151
- At runtime the global `Rest` registry tries each controller in order:
152
-
153
- ```ts
154
- const hit = Rest.dispatch(req); // Response | null
155
- if (hit != null) return hit; // first matching route's Response
156
- return Response.unhandled(); // no route matched
157
- ```
158
-
159
- `RestHandler` is a ready-made handler that does exactly this, so a REST-only app
160
- needs no custom handler:
161
-
162
- ```ts
163
- import { RestHandler } from 'toiljs/server/runtime';
164
- Server.handler = () => new RestHandler();
165
- ```
166
-
167
- `Response.unhandled()` is a `404` carrying the `x-toil-unhandled` marker header.
168
- On the dev server and edge that marker means "no route matched here" and lets
169
- the request fall through to the next layer (Vite in dev, static/SSR on the
170
- edge). A deliberate `Response.notFound()` does **not** carry the marker and is
171
- sent to the client verbatim.
172
-
173
- ---
174
-
175
- ## `Request`
176
-
177
- The decoded incoming request (`server/runtime/request.ts`).
178
-
179
- ### Fields
180
-
181
- | Field | Type | Notes |
182
- | --- | --- | --- |
183
- | `method` | `Method` | `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`, `UNKNOWN`. |
184
- | `path` | `string` | Path including the query string. |
185
- | `headers` | `Array<Header>` | Ordered; a `Header` is `{ name, value }`. |
186
- | `body` | `Uint8Array` | Raw request body bytes. |
187
-
188
- ### Methods
189
-
190
- | Method | Signature | Notes |
191
- | --- | --- | --- |
192
- | `header` | `header(name: string): string \| null` | Case-insensitive lookup, `null` if absent. |
193
- | `cookies` | `cookies(): CookieMap` | Parses the `Cookie` header (percent-decoded values); cached for the request. |
194
- | `cookie` | `cookie(name: string): string \| null` | A single cookie value, or `null`. |
195
-
196
- The `Method` enum and `Header` class are exported from
197
- `toiljs/server/runtime`.
198
-
199
- ## `RouteContext`
200
-
201
- Passed to any route method that declares a `RouteContext` parameter
202
- (`server/runtime/rest/RouteContext.ts`).
203
-
204
- | Member | Signature | Notes |
205
- | --- | --- | --- |
206
- | `request` | `Request` | The raw incoming request. |
207
- | `param` | `param(name: string): string` | Captured path param; `""` if absent. |
208
- | `query` | `query(name: string): string` | Query-string value; `""` if absent. Not URL-decoded in v1. |
209
- | `header` | `header(name: string): string \| null` | Case-insensitive request header. |
210
- | `text` | `text(): string` | The request body decoded as UTF-8. |
211
-
212
- ## `Response`
213
-
214
- The outgoing response builder (`server/runtime/response.ts`). Construct one with
215
- a static factory, then chain instance methods (each returns the same `Response`).
216
-
217
- ### Constructor
218
-
219
- ```ts
220
- new Response(status: u16, body: Uint8Array, headers: Array<Header> | null = null)
221
- ```
222
-
223
- ### Static factories
224
-
225
- | Factory | Signature | Status | Content-Type |
226
- | --- | --- | --- | --- |
227
- | `Response.text` | `text(body: string, status: u16 = 200)` | 200 | `text/plain; charset=utf-8` |
228
- | `Response.html` | `html(body: string, status: u16 = 200)` | 200 | `text/html; charset=utf-8` |
229
- | `Response.json` | `json(body: string, status: u16 = 200)` | 200 | `application/json; charset=utf-8` |
230
- | `Response.bytes` | `bytes(body: Uint8Array, status: u16 = 200)` | 200 | `application/octet-stream` |
231
- | `Response.empty` | `empty(status: u16)` | custom | (none) |
232
- | `Response.notFound` | `notFound()` | 404 | text |
233
- | `Response.badRequest` | `badRequest(msg = 'bad request')` | 400 | text |
234
- | `Response.internalError` | `internalError(msg = 'internal error')` | 500 | text |
235
- | `Response.unhandled` | `unhandled()` | 404 | text + `x-toil-unhandled` marker |
236
-
237
- `json` takes an already-serialized string; build it with `DataWriter`-free JSON
238
- or a `@data` type's `toJSON().toString()`. For binary, prefer `bytes`.
239
-
240
- ### Instance methods
241
-
242
- | Method | Signature | Notes |
243
- | --- | --- | --- |
244
- | `setHeader` | `setHeader(name: string, value: string): Response` | Appends a header (repeatable). |
245
- | `setCookie` | `setCookie(cookie: Cookie): Response` | Appends a `Set-Cookie`. Call again for more. |
246
- | `setCookieKV` | `setCookieKV(name: string, value: string): Response` | Shorthand for `setCookie(new Cookie(name, value))`. |
247
- | `clearCookie` | `clearCookie(name: string, path = '/', domain = ''): Response` | Emits a deletion `Set-Cookie` (empty value, `Max-Age=0`). |
248
- | `cache` | `cache(edgeTtlMinutes: u16, browserTtlSeconds: u32 = 0, privateScope: bool = false, allowAuth: bool = false): Response` | Marks the response cacheable. See [Caching](./caching.md). |
249
- | `cacheFor` | `cacheFor(minutes: u16): Response` | Shorthand for `cache(minutes)` (edge only). |
250
-
251
- ```ts
252
- return Response.json('{"id":42}')
253
- .setHeader('x-trace', traceId)
254
- .setCookie(Cookie.create('sid', token).httpOnly().secure())
255
- .cacheFor(5);
256
- ```
257
-
258
- See [Cookies](./cookies.md) for the cookie builder, and [Caching](./caching.md)
259
- for the cache directives.
package/docs/rpc.md DELETED
@@ -1,149 +0,0 @@
1
- # RPC and the generated client
2
-
3
- The server build with `--rpcModule shared/server.ts` scans your decorated
4
- surface (`@data`, `@user`, `@service`/`@remote`, `@rest`) and emits one
5
- TypeScript module: a typed `Server` proxy, the `@data` codec classes, the REST
6
- fetch client, and the `getUser()` accessor. The client imports that file and
7
- calls the server with full type-safety and editor autocomplete. The file is
8
- regenerated on every server build, so it never drifts from the server.
9
-
10
- ```sh
11
- toilscript --target release --rpcModule shared/server.ts
12
- ```
13
-
14
- ## `@service` and `@remote`
15
-
16
- A `@service` class exposes its `@remote` methods as callable RPC. A top-level
17
- `@remote` function is exposed directly.
18
-
19
- ```ts
20
- @service
21
- class Stats {
22
- @remote
23
- public playerCount(): i32 { return store.size; }
24
- }
25
-
26
- @remote
27
- function ping(n: i32): i32 { return n + 1; }
28
- ```
29
-
30
- The generated client surfaces these on the global `Server` proxy. A service is
31
- keyed by its class name with the first letter lowercased (`Stats` → `stats`):
32
-
33
- ```ts
34
- await Server.stats.playerCount(); // Promise<number>
35
- await Server.ping(42); // Promise<number>
36
- ```
37
-
38
- Arguments and return values are `@data`-typed; scalars map to TS as below.
39
-
40
- ## The generated `Server` surface
41
-
42
- `shared/server.ts` declares a global `Server` whose shape is, schematically:
43
-
44
- ```ts
45
- declare global {
46
- const Server: {
47
- // top-level @remote functions
48
- ping(n: number): Promise<number>;
49
-
50
- // @service classes (keyed by lowercased name)
51
- readonly stats: {
52
- playerCount(): Promise<number>;
53
- };
54
-
55
- // @rest controllers, under REST
56
- readonly REST: {
57
- readonly players: {
58
- get(args: { params: { id: string | number | bigint }; query?: …; headers?: … }): Promise<Response>;
59
- create(args: { body: NewPlayer; query?: …; headers?: … }): Promise<Player>;
60
- };
61
- };
62
- };
63
- }
64
- ```
65
-
66
- ### Type mapping (ToilScript → TypeScript)
67
-
68
- | ToilScript | TypeScript |
69
- | --- | --- |
70
- | `u8`,`u16`,`u32`,`i8`,`i16`,`i32`,`f32`,`f64` | `number` |
71
- | `u64`,`i64`,`u128`,`i128`,`u256`,`i256` | `bigint` |
72
- | `bool` | `boolean` |
73
- | `string` | `string` |
74
- | a `@data` class `T` | `T` (the emitted class) |
75
- | `T[]` | `T[]` |
76
-
77
- 64-bit-and-larger integers are `bigint` on the client and travel as decimal
78
- strings on the JSON wire, so they are exact at any magnitude.
79
-
80
- ### Emitted `@data` classes
81
-
82
- Each `@data` (and `@user`) class becomes an exported TS class with the fields, a
83
- defaulted constructor, and the matching codec:
84
-
85
- ```ts
86
- export class Player {
87
- constructor(public username = '', public admin = false, public score = 0n) {}
88
- encodeInto(w: DataWriter): void { /* … */ }
89
- encode(): Uint8Array { /* dataId prefix + fields */ }
90
- static decodeFrom(r: DataReader): Player { /* … */ }
91
- static decode(buf: Uint8Array): Player { /* … */ }
92
- static dataId(): number { /* FNV-1a of "Player" */ }
93
- static fromJSONValue(v: any): Player { /* revive, 64-bit from strings */ }
94
- toJSONValue(): any { /* 64-bit as decimal strings */ }
95
- }
96
- ```
97
-
98
- The codec is byte-compatible with the server's `@data` codec, so binary bodies
99
- round-trip exactly between client and wasm.
100
-
101
- ## The REST fetch client
102
-
103
- Every `@rest` route also gets a typed fetch wrapper under `Server.REST.<key>`,
104
- keyed by the controller name lowercased. The call argument is an object:
105
-
106
- ```ts
107
- Server.REST.players.create({
108
- body: new NewPlayer('alice'), // present iff the route takes a body
109
- // params: { id: 7 }, // present iff the path has :params
110
- query: { ref: 'home' }, // optional
111
- headers: { 'x-trace': traceId }, // optional
112
- });
113
- ```
114
-
115
- - If the route has no params and no body, the whole argument is optional
116
- (`args?`).
117
- - The wrapper builds the URL (substituting `:params`, appending `query`),
118
- `fetch`es with `credentials` as configured, throws on a non-2xx status, and
119
- decodes the response into the route's return type.
120
- - A route declared to return `Response` resolves to the raw `fetch` `Response`,
121
- so you can stream or inspect headers yourself.
122
-
123
- ```ts
124
- const player = await Server.REST.players.create({ body: new NewPlayer('alice') });
125
- // ^? Player
126
- ```
127
-
128
- ## `getUser()`
129
-
130
- When the server declares a `@user` class, the generated module also exports a
131
- typed, no-argument `getUser()` that reads the readable companion cookie and
132
- decodes it with the generated codec:
133
-
134
- ```ts
135
- import { getUser } from './shared/server';
136
-
137
- const user = getUser(); // Account | null, fully typed
138
- ```
139
-
140
- This is **display-only**: the server re-verifies the signed session on every
141
- `@auth` request. See [Auth](./auth.md) for the full picture.
142
-
143
- ## Notes
144
-
145
- - `shared/server.ts` is generated; never edit it by hand. Re-run the server
146
- build (or `toiljs dev`, which does it on save) to refresh it.
147
- - The `Server` proxy is declared as an ambient global on the client; the runtime
148
- implementation is provided by toiljs. The REST client and `getUser` are real
149
- exported values in the generated module.
package/docs/server.md DELETED
@@ -1,61 +0,0 @@
1
- # Server (toilscript → WebAssembly)
2
-
3
- `server/` is the toilscript source, compiled to WebAssembly by `toilscript`.
4
-
5
- - `server/main.ts`, the `@main` entry, exported as the WASM `main`.
6
- - `server/index.ts`, your functions.
7
- - `server/tsconfig.json`, extends `toilscript/std/assembly.json` (AssemblyScript/toilscript
8
- globals like `i32`, not the DOM), so editors resolve server types correctly.
9
- - `npm run build:server` (or `npm run build`) emits `build/server/release.wasm` and
10
- regenerates `shared/server.ts` (the typed client RPC module).
11
-
12
- ## Typed RPC (`@data` / `@remote` / `@service`)
13
-
14
- Tag server code and the build generates a typed client `Server` surface:
15
-
16
- - `@data class X {}`, a serializable struct. Generates a client class with the same fields
17
- plus `encode`/`decode`; construct it on the client: `import { X } from "shared/server"`.
18
- - `@remote function f(a: T): R`, a client-callable endpoint, becomes `Server.f(a)`.
19
- - `@service class S { @remote m(...) {} }`, namespaces methods: `Server.s.m(...)`.
20
-
21
- On the client, `Server` is a global (no import) and fully typed; every call is async
22
- (`Promise<R>`). Inputs/outputs are scalars, arrays, or `@data` classes, both directions.
23
-
24
- Note: the client↔server transport is not wired yet, so calling a `Server` method throws
25
- until it lands; the typed surface + codec are generated and ready.
26
-
27
- ## HTTP REST (`@rest` / `@route`)
28
-
29
- Tag a class `@rest` and its methods with a verb to expose a real HTTP API. Unlike RPC,
30
- the generated client is working `fetch` code (it is just HTTP).
31
-
32
- - `@rest("api") class Todos {}`, mounts the controller at `/api` (bare `@rest` → `/`).
33
- - `@get("/todos/:id")` / `@post` / `@del` / `@put` / `@patch` / `@head` / `@options`, verb
34
- shortcuts; or `@route({ method: Methods.GET, path: "/todos", stream: DataStream.JSON })`.
35
- - A method takes an optional `@data` body + an optional `ctx: RouteContext` (path params via
36
- `ctx.param("id")`, `ctx.query(...)`, `ctx.header(...)`). It returns either a `@data` type,
37
- which the compiler encodes per `stream` (`DataStream.JSON` default, or `DataStream.Binary`,
38
- lossless for large `u64`/bignum), or a `Response` for full control - custom status and
39
- headers, e.g. `Response.json(value.toJSON().toString()).setHeader("cache-control", "no-store")`
40
- or `Response.notFound()`. (The editor sees the compiler-injected `@data` `toJSON`/`encode`
41
- members via the toilscript plugin, so serializing into a `Response` is editor-clean.)
42
-
43
- Each `@rest` class self-registers; dispatch them from your handler - it composes, it never
44
- takes over `handle()`:
45
-
46
- ```ts
47
- import { ToilHandler, Request, Response, Rest } from "toiljs/server/runtime";
48
- export class App extends ToilHandler {
49
- public handle(req: Request): Response {
50
- const hit = Rest.dispatch(req); // try every @rest controller
51
- if (hit != null) return hit;
52
- return Response.notFound(); // your own logic / static fallback
53
- }
54
- }
55
- ```
56
-
57
- For a REST-only project, `Server.handler = () => new RestHandler()` does the same with no
58
- boilerplate. On the client: `Server.REST.todos.getTodo({ params: { id } })` (see [client.md](./client.md)).
59
-
60
- For the full reference (`@rest`/verb decorators, `RouteContext`, `Request`, `Response`,
61
- dispatch + the 404 fallback) see [routing.md](./routing.md).