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/streams.md DELETED
@@ -1,178 +0,0 @@
1
- # Streams
2
-
3
- A `@stream` declares a long-lived, stateful protocol handler over WebTransport -
4
- the **L2/L3** (regional / continental) stream tier of the Toil edge. Unlike a
5
- `@rest` route, which is a fresh handler per request, a `@stream` is a **resident
6
- WebAssembly box per connection**: it is created when the connection opens, lives
7
- for the whole connection, and is torn down on close. State stored on its fields
8
- **persists across events**, because it is the same box every time.
9
-
10
- ```ts
11
- @stream('echo')
12
- class Echo {
13
- private count: i32 = 0;
14
-
15
- @connect
16
- onConnect(): void {
17
- this.count = 0;
18
- }
19
-
20
- @message
21
- onMessage(): void {
22
- this.count = this.count + 1;
23
- }
24
-
25
- @close
26
- onClose(): void {}
27
- }
28
- ```
29
-
30
- ## Declaring a stream
31
-
32
- `@stream(name)` marks a class as a stream handler and mounts it at the given
33
- name/route. The class becomes a resident box; its fields are the connection's
34
- state.
35
-
36
- ```ts
37
- @stream('echo') // mounted at /echo
38
- class Echo { /* ... */ }
39
- ```
40
-
41
- A stream lives on the **L2/L3 stream tier** and its default scope is **Regional
42
- (L2)**. See [Tiers](./tiers.md) for the full tier model.
43
-
44
- ## Lifecycle hooks
45
-
46
- A stream method is a lifecycle hook, chosen by its decorator. All hooks are
47
- optional - declare only the ones you need; a missing hook is a no-op.
48
-
49
- | Decorator | Fires when |
50
- | --- | --- |
51
- | `@connect` | the connection opens (the box has just been created). |
52
- | `@message` | an inbound frame arrives. |
53
- | `@close` | the connection closes gracefully (the box is torn down after this hook). |
54
- | `@disconnect` | the transport is lost abruptly. |
55
-
56
- The `Echo` example above shows why state survives: `count` is set to `0` in
57
- `@connect`, incremented on every `@message`, and the increments **accumulate**.
58
- That is only possible because the same resident box handles every event for the
59
- connection. A `@rest` handler's fields would reset on each request, since a
60
- fresh handler is constructed per request.
61
-
62
- Distributed stream channels are not part of the live v1 ABI. The edge rejects
63
- stream artifacts that declare a channel hook until the channel fan-out runtime
64
- exists.
65
-
66
- ## Placement
67
-
68
- A `@stream` is distributed across the eligible L2/L3 stream nodes and pinned to
69
- **ONE worker** for the connection's lifetime via QUIC connection-id steering. The
70
- connection always lands on the same worker, so the box - and the state on its
71
- fields - survives every event. You do not manage placement; the edge steers each
72
- connection to its resident box automatically.
73
-
74
- ## The entry: `main.stream.ts`
75
-
76
- The stream surface has its own entry, `server/main.stream.ts`, distinct from the
77
- request entry (`server/main.ts`). It re-exports the WASM runtime exports and
78
- imports the `@stream` classes, which pulls their compiler-generated
79
- `stream_dispatch` export into the artifact.
80
-
81
- ```ts
82
- import { revertOnError } from 'toiljs/server/runtime/abort/abort';
83
-
84
- import './streams/Echo';
85
-
86
- // Re-export the WASM entry points the host binds, exactly like main.ts.
87
- export * from 'toiljs/server/runtime/exports';
88
- export function abort(message: string, fileName: string, line: u32, column: u32): void {
89
- revertOnError(message, fileName, line, column);
90
- }
91
- ```
92
-
93
- This entry compiles into its **own artifact**, `build/server/release-stream.wasm`
94
- - the resident stream box - separate from the request build,
95
- `build/server/release.wasm`. Add a stream as you grow by importing it here:
96
-
97
- ```ts
98
- import './streams/Echo';
99
- ```
100
-
101
- ## Build
102
-
103
- `toiljs build` produces `release-stream.wasm` automatically when the project
104
- declares a `@stream` surface. The single build runs one toilscript pass per tier,
105
- handing each pass only the entries that belong to it, so `release.wasm` never
106
- contains `stream_dispatch` and the stream artifact never contains the request
107
- `handle`. Plain `@data` and helper modules are shared into every artifact.
108
-
109
- ```sh
110
- $ toiljs build
111
- $ ls build/server/*.wasm
112
- build/server/release.wasm # L1 request (exports: handle)
113
- build/server/release-stream.wasm # L2/L3 stream (exports: stream_dispatch)
114
- build/server/release-cold.wasm # L4 daemon (exports: daemon_start, scheduled_tick)
115
- ```
116
-
117
- See [Tiers](./tiers.md) for how the three artifacts map to the deployment tiers.
118
-
119
- ## Reading and replying to messages
120
-
121
- `@message` receives the inbound frame as a `StreamPacket` and returns a
122
- `StreamOutbound`. `StreamPacket.bytes()` is the raw frame payload;
123
- `StreamOutbound.reply(bytes)` stages one frame back to the client (return an empty
124
- `StreamOutbound` to accept the frame without replying). The same resident box
125
- handles every frame, so state on its fields persists across messages.
126
-
127
- ```ts
128
- @message
129
- reply(packet: StreamPacket): StreamOutbound {
130
- return StreamOutbound.reply(packet.bytes()); // echo the bytes back
131
- }
132
- ```
133
-
134
- ## Typed messages
135
-
136
- By default a `@message` payload is **raw bytes**. Opt into a decoded `@data` value
137
- with `@stream({ message: T })`: the `@message` hook then receives the named `@data`
138
- class, decoded from the frame for you. The reply stays raw (`StreamOutbound`).
139
-
140
- ```ts
141
- @data
142
- class ChatMsg { text: string = ''; }
143
-
144
- @stream({ message: ChatMsg })
145
- class Chat {
146
- @message
147
- onMessage(msg: ChatMsg): StreamOutbound { // decoded @data, not raw bytes
148
- return StreamOutbound.reply(new TextEncoder().encode(msg.text));
149
- }
150
- }
151
- ```
152
-
153
- ## The client
154
-
155
- A `@stream` class is reachable from the browser as `Server.Stream.<ClassName>`. The
156
- typed client is generated into `shared/server.ts` (the same place `Server.REST`
157
- lands), so no manual wiring is needed. `connect()` opens a WebSocket to the class's
158
- route and resolves a channel:
159
-
160
- ```ts
161
- const chat = await Server.Stream.Chat.connect();
162
- chat.onMessage((bytes) => { /* a reply frame, always raw bytes */ });
163
- chat.send(new ChatMsg('hello')); // a typed stream: send() encodes the @data for you
164
- chat.onClose((code) => { /* a 0x02xx stream close code */ });
165
- chat.close();
166
- ```
167
-
168
- - The channel key is the **class name** (`Server.Stream.Chat`); it connects to the
169
- class's mount route (`/Chat`).
170
- - A **raw** `@stream` channel sends `Uint8Array`; a **typed** `@stream({ message: T })`
171
- channel sends the `@data` class and encodes it on the wire for you.
172
- - The inbound reply is **always raw bytes** - the server's `StreamOutbound` is raw.
173
- - `connect()` resolves once the upgrade completes; a `@connect` reject (or any
174
- later server close) surfaces through `onClose(code)`.
175
-
176
- ---
177
-
178
- See also: [Tiers](./tiers.md), [Daemon](./daemon.md), [Routing](./routing.md).
package/docs/styling.md DELETED
@@ -1,22 +0,0 @@
1
- # Styling
2
-
3
- The app imports one stylesheet from `client/toil.tsx` (e.g. `./styles/main.css`).
4
-
5
- ## Preprocessors & Tailwind
6
-
7
- Pick a CSS preprocessor (none / Sass / Less / Stylus) and optionally Tailwind at
8
- `toiljs create`, or change it later on an existing project:
9
-
10
- ```sh
11
- toiljs configure # interactive
12
- toiljs configure --tailwind # add Tailwind
13
- toiljs configure --style sass # switch preprocessor
14
- ```
15
-
16
- `configure` installs/removes the right packages and rewrites the imports. Tailwind lives
17
- in its own `styles/tailwind.css` (`@import "tailwindcss";`).
18
-
19
- ## Imports
20
-
21
- `.css` / `.scss` / `.sass` / `.less` / `.styl` and image imports (`.svg`, `.png`, …) are
22
- typed via `toil-env.d.ts`.
package/docs/tiers.md DELETED
@@ -1,133 +0,0 @@
1
- # Deployment tiers
2
-
3
- A Toil app's server runs across several deployment **tiers** from one source
4
- tree. Each tier has a different lifetime and placement on the edge, and compiles
5
- into its own WebAssembly artifact. You write one project; `toiljs build` decides
6
- which entries belong to which tier and emits one `.wasm` per tier. You opt into a
7
- tier purely by adding its entry file and surface decorator; nothing else changes.
8
-
9
- ## The tiers
10
-
11
- | Entry (`server/`) | Surface | Artifact | Tier | Lifetime / placement |
12
- | ----------------- | -------------------------------- | ---------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
13
- | `main.ts` | `@rest` / `@service` / `@remote` | `build/server/release.wasm` | **L1** request | A fresh handler per request, anywhere on the edge. |
14
- | `main.stream.ts` | `@stream` | `build/server/release-stream.wasm` | **L2/L3** stream | One resident box per connection, pinned to a worker via QUIC connection-id steering; its state survives every event. See [Streams](./streams.md). |
15
- | `main.daemon.ts` | `@daemon` / `@scheduled` | `build/server/release-cold.wasm` | **L4** daemon | Exactly one leader-elected box per domain (warm standby, at-most-once failover) firing `@scheduled` tasks. See [Daemon](./daemon.md). |
16
-
17
- The three tiers differ in how long a box lives and how many of it exist:
18
-
19
- - **L1 request** is stateless. A `@rest` handler's fields reset each request,
20
- because a fresh box serves each one, anywhere on the edge.
21
- - **L2/L3 stream** is resident per connection. A `@stream` box is created when a
22
- connection opens, lives for its lifetime, and is torn down on close, so its
23
- fields persist across every event.
24
- - **L4 daemon** is a single elected leader per domain - the global coordination
25
- tier - running recurring background work on a cadence.
26
-
27
- ## How the build works
28
-
29
- `toiljs build` runs one toilscript pass per tier, handing each pass only the
30
- entries that belong to it. Tier membership is decided by the surface decorator or
31
- by the entry naming convention:
32
-
33
- - a runtime-export entry that is **not** `*.stream.ts` or `*.daemon.ts` is the
34
- **request** entry (`main.ts`), which compiles `@rest` / `@service` / `@remote`;
35
- - `*.stream.ts` is the **stream** entry, which compiles `@stream`;
36
- - `*.daemon.ts` is the **daemon** entry, which compiles `@daemon` / `@scheduled`.
37
-
38
- Plain `@data` and helper modules carry no tier of their own, so they are shared
39
- into every artifact. Routing each entry to exactly one tier is what keeps
40
- `release.wasm` free of `stream_dispatch` and keeps the daemon artifact free of
41
- the request `handle`.
42
-
43
- Each entry is a thin file that imports its tier's modules and re-exports the
44
- right runtime hooks. The stream and request entries re-export the request runtime
45
- exports; the daemon entry does not, because a cold artifact exposes
46
- `daemon_start` / `scheduled_tick`, not `handle`:
47
-
48
- ```ts
49
- // server/main.stream.ts - the L2/L3 stream entry
50
- import { revertOnError } from 'toiljs/server/runtime/abort/abort';
51
- import './streams/Echo';
52
-
53
- export * from 'toiljs/server/runtime/exports';
54
- export function abort(message: string, fileName: string, line: u32, column: u32): void {
55
- revertOnError(message, fileName, line, column);
56
- }
57
- ```
58
-
59
- ```ts
60
- // server/main.daemon.ts - the L4 daemon entry
61
- import { revertOnError } from 'toiljs/server/runtime/abort/abort';
62
- import './daemon/Jobs';
63
-
64
- // NOTE: no `export *` from the request runtime - a cold artifact exposes
65
- // daemon_start/scheduled_tick, not the request `handle`.
66
- export function abort(message: string, fileName: string, line: u32, column: u32): void {
67
- revertOnError(message, fileName, line, column);
68
- }
69
- ```
70
-
71
- A single build produces the artifacts side by side:
72
-
73
- ```sh
74
- $ ls build/server/*.wasm
75
- build/server/release.wasm # L1 request (exports: handle)
76
- build/server/release-stream.wasm # L2/L3 stream (exports: stream_dispatch)
77
- build/server/release-cold.wasm # L4 daemon (exports: daemon_start, scheduled_tick)
78
- ```
79
-
80
- ## Single-artifact default
81
-
82
- A project with no `@stream` and no `@daemon` surface keeps the default
83
- single-artifact build - just `build/server/release.wasm`. The stream and daemon
84
- tiers are opt-in: add `main.stream.ts` (and a `@stream` class) to get
85
- `release-stream.wasm`, add `main.daemon.ts` (and a `@daemon` class) to get
86
- `release-cold.wasm`. Existing request-only apps build exactly as before.
87
-
88
- ## When to use each tier
89
-
90
- - **L1 request** for request/response and RPC: `@rest` controllers, `@service` /
91
- `@remote` callable surface. The default tier; most code lives here.
92
- - **L2/L3 stream** for stateful, long-lived connections where per-connection
93
- state must survive across events - the resident box is pinned to one worker for
94
- the connection's lifetime.
95
- - **L4 daemon** for scheduled and coordination work: rollups, cleanup, polling an
96
- upstream, anything that should run exactly once per domain on a cadence rather
97
- than per request.
98
-
99
- ```ts
100
- // server/streams/Echo.ts - L2/L3: the box is resident, so `count` persists.
101
- @stream('echo')
102
- class Echo {
103
- private count: i32 = 0;
104
-
105
- @connect onConnect(): void {
106
- this.count = 0;
107
- }
108
- @message onMessage(): void {
109
- this.count = this.count + 1;
110
- }
111
- @close onClose(): void {
112
- /* box torn down after this hook */
113
- }
114
- }
115
- ```
116
-
117
- ```ts
118
- // server/daemon/Jobs.ts - L4: one leader per domain runs this hourly.
119
- @daemon
120
- class Jobs {
121
- @scheduled('1h')
122
- hourly(): void {
123
- // Recurring background work: rollups, cleanup, polling an upstream, ...
124
- }
125
- }
126
- ```
127
-
128
- ## See also
129
-
130
- - [Streams](./streams.md) - the `@stream` surface and the L2/L3 tier.
131
- - [Daemon](./daemon.md) - the `@daemon` surface and the L4 tier.
132
- - [Routing](./routing.md) - `@rest` controllers on the L1 request tier.
133
- - [RPC](./rpc.md) - `@service` / `@remote` and the generated client.
package/docs/time.md DELETED
@@ -1,43 +0,0 @@
1
- # Time
2
-
3
- `Time` is the guest's wall-clock. It is the toiljs-blessed way to read the
4
- current time, backed by the host's `Date.now()` binding (`env.Date.now`). Both
5
- the edge and the dev server provide that binding, so time behaves identically in
6
- `toiljs dev` and in production.
7
-
8
- It is available as an ambient global (`@global`, no import) and is also exported
9
- from `toiljs/server/runtime`.
10
-
11
- ```ts
12
- import { Time } from 'toiljs/server/runtime'; // optional; Time is also a global
13
-
14
- const ms = Time.nowMillis(); // u64 milliseconds since the Unix epoch
15
- const s = Time.nowSeconds(); // u64 whole seconds since the Unix epoch
16
- ```
17
-
18
- ## API
19
-
20
- | Member | Signature | Description |
21
- | --- | --- | --- |
22
- | `Time.nowMillis()` | `static nowMillis(): u64` | Milliseconds since the Unix epoch (the raw host `Date.now()` value). |
23
- | `Time.nowSeconds()` | `static nowSeconds(): u64` | Whole seconds since the epoch (`nowMillis() / 1000`). The unit used by sessions and login challenges. |
24
-
25
- ## Semantics
26
-
27
- `Time` is **wall-clock, not monotonic**, exactly like browser `Date.now()`. It
28
- tracks the system clock and can step backward across an NTP correction.
29
-
30
- - Use it to stamp and compare absolute instants: session `iat`/`exp`, login
31
- challenge expiry, cache ages.
32
- - Do **not** use it to measure elapsed time or as a high-resolution timer; a
33
- backward step would produce a negative or zero interval.
34
-
35
- ## Relationship to `Date.now()`
36
-
37
- ToilScript's `Date.now()` lowers to the same `env.Date.now` host import, so you
38
- *can* call it directly. Prefer `Time`: it makes the host boundary (and the
39
- single millisecond unit) explicit and easy to find, and it gives you
40
- `nowSeconds()` without an open-coded `/ 1000` cast at every call site.
41
-
42
- `AuthService` uses `Time.nowSeconds()` internally for session `iat`/`exp`, so
43
- session timing and any timing you do in a handler share one clock.