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.
- package/CHANGELOG.md +5 -0
- package/README.md +2 -2
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/index.js +303 -293
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/config.d.ts +2 -0
- package/build/compiler/config.js +1 -0
- package/build/compiler/docs.js +8 -24
- package/build/compiler/generate.js +4 -2
- package/build/compiler/index.d.ts +1 -1
- package/build/compiler/index.js +69 -6
- package/build/compiler/toil-docs.generated.js +64 -22
- package/build/devserver/.tsbuildinfo +1 -1
- package/build/devserver/analytics/index.js +7 -3
- package/build/devserver/db/database.d.ts +4 -0
- package/build/devserver/db/database.js +43 -1
- package/build/devserver/db/index.d.ts +1 -1
- package/build/devserver/db/index.js +1 -1
- package/build/devserver/db/types.d.ts +3 -0
- package/build/devserver/db/types.js +2 -0
- package/build/devserver/runtime/module.js +4 -1
- package/docs/README.md +104 -65
- package/docs/auth/README.md +102 -0
- package/docs/auth/configuration.md +94 -0
- package/docs/auth/extending.md +202 -0
- package/docs/auth/how-it-works.md +138 -0
- package/docs/auth/usage.md +188 -0
- package/docs/backend/README.md +143 -0
- package/docs/backend/data.md +351 -0
- package/docs/backend/rest.md +402 -0
- package/docs/backend/rpc.md +226 -0
- package/docs/background/README.md +114 -0
- package/docs/background/daemons.md +230 -0
- package/docs/background/derive.md +179 -0
- package/docs/cli/README.md +377 -0
- package/docs/concepts/config.md +416 -0
- package/docs/concepts/decorators.md +127 -0
- package/docs/concepts/security.md +108 -0
- package/docs/concepts/tiers.md +166 -0
- package/docs/concepts/types.md +216 -0
- package/docs/database/README.md +143 -0
- package/docs/database/capacity.md +350 -0
- package/docs/database/counters.md +174 -0
- package/docs/database/documents.md +255 -0
- package/docs/database/events.md +307 -0
- package/docs/database/membership.md +246 -0
- package/docs/database/setup.md +155 -0
- package/docs/database/unique.md +216 -0
- package/docs/database/views.md +246 -0
- package/docs/frontend/README.md +101 -0
- package/docs/frontend/data-fetching.md +243 -0
- package/docs/frontend/images.md +148 -0
- package/docs/frontend/metadata.md +344 -0
- package/docs/frontend/rendering.md +236 -0
- package/docs/frontend/routing.md +344 -0
- package/docs/frontend/scripts.md +118 -0
- package/docs/frontend/search.md +191 -0
- package/docs/frontend/styling.md +147 -0
- package/docs/getting-started/README.md +81 -0
- package/docs/getting-started/create-project.md +131 -0
- package/docs/getting-started/deploy.md +101 -0
- package/docs/getting-started/first-app.md +215 -0
- package/docs/getting-started/installation.md +106 -0
- package/docs/getting-started/migrating.md +125 -0
- package/docs/getting-started/project-structure.md +163 -0
- package/docs/introduction/README.md +41 -0
- package/docs/introduction/design-principles.md +55 -0
- package/docs/introduction/distributed.md +74 -0
- package/docs/introduction/how-it-works.md +74 -0
- package/docs/introduction/hyperscale.md +51 -0
- package/docs/introduction/modern-stack.md +36 -0
- package/docs/introduction/vs-other-frameworks.md +48 -0
- package/docs/introduction/why-toil.md +93 -0
- package/docs/llms.txt +90 -0
- package/docs/realtime/README.md +102 -0
- package/docs/realtime/channels.md +211 -0
- package/docs/realtime/streams.md +369 -0
- package/docs/services/README.md +60 -0
- package/docs/services/analytics.md +268 -0
- package/docs/services/caching.md +175 -0
- package/docs/services/cookies.md +235 -0
- package/docs/services/crypto.md +209 -0
- package/docs/services/email.md +289 -0
- package/docs/services/environment.md +141 -0
- package/docs/services/ratelimit.md +174 -0
- package/docs/services/time.md +85 -0
- package/examples/basic/client/routes/analytics.tsx +13 -12
- package/examples/basic/server/models/SiteAnalytics.ts +29 -17
- package/examples/basic/server/routes/Analytics.ts +15 -17
- package/examples/basic/server/routes/UserId.ts +22 -0
- package/package.json +15 -11
- package/scripts/gen-toil-docs.mjs +24 -35
- package/server/auth/AuthController.ts +336 -0
- package/server/auth/AuthUser.ts +23 -0
- package/server/auth/index.ts +16 -0
- package/server/globals/auth.ts +31 -0
- package/server/globals/userid.ts +107 -0
- package/src/compiler/config.ts +13 -0
- package/src/compiler/docs.ts +16 -33
- package/src/compiler/generate.ts +6 -2
- package/src/compiler/index.ts +114 -6
- package/src/compiler/toil-docs.generated.ts +64 -22
- package/src/devserver/analytics/index.ts +10 -4
- package/src/devserver/db/database.ts +67 -1
- package/src/devserver/db/index.ts +1 -0
- package/src/devserver/db/types.ts +13 -0
- package/src/devserver/runtime/module.ts +7 -0
- package/test/analytics-dev.test.ts +2 -1
- package/test/devserver-database.test.ts +113 -0
- package/docs/auth-todo.md +0 -149
- package/docs/auth.md +0 -322
- package/docs/caching.md +0 -115
- package/docs/cli.md +0 -17
- package/docs/client.md +0 -39
- package/docs/cookies.md +0 -457
- package/docs/crypto.md +0 -130
- package/docs/daemon.md +0 -123
- package/docs/data.md +0 -131
- package/docs/derive.md +0 -159
- package/docs/email.md +0 -326
- package/docs/environment.md +0 -97
- package/docs/getting-started.md +0 -128
- package/docs/index.md +0 -30
- package/docs/ratelimit.md +0 -95
- package/docs/routing.md +0 -259
- package/docs/rpc.md +0 -149
- package/docs/server.md +0 -61
- package/docs/ssr.md +0 -632
- package/docs/streams.md +0 -178
- package/docs/styling.md +0 -22
- package/docs/tiers.md +0 -133
- 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.
|