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
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Time
|
|
2
|
+
|
|
3
|
+
**`Time` is the edge's clock: the blessed way to read the current time in your backend.** It gives you the number of milliseconds or seconds since a fixed reference point, so you can stamp events and compute expiries.
|
|
4
|
+
|
|
5
|
+
## How to use it
|
|
6
|
+
|
|
7
|
+
`Time` is an ambient global (no import needed), and is also exported from `toiljs/server/runtime`:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const ms = Time.nowMillis(); // milliseconds since the Unix epoch (a u64)
|
|
11
|
+
const s = Time.nowSeconds(); // whole seconds since the Unix epoch (a u64)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| Member | Returns | Description |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| `Time.nowMillis()` | `u64` | Milliseconds since the **Unix epoch** (midnight UTC on 1 January 1970). This is the same value as JavaScript's `Date.now()`. |
|
|
17
|
+
| `Time.nowSeconds()` | `u64` | Whole seconds since the epoch (`nowMillis() / 1000`). This is the unit sessions and login challenges use. |
|
|
18
|
+
|
|
19
|
+
The **Unix epoch** is just the agreed "time zero" that computers count from. "Milliseconds since the epoch" is a single number that unambiguously names an instant, with no time zone to worry about.
|
|
20
|
+
|
|
21
|
+
## Why time is a "host import" (and what that means)
|
|
22
|
+
|
|
23
|
+
Your backend runs as a **WebAssembly** module: a small, sandboxed program. A sandbox is deliberately sealed off from the outside world. It has no clock of its own, no network, no files, nothing except the memory it was given and a short list of functions the host chooses to hand it. Each of those handed-in functions is called a **host import**.
|
|
24
|
+
|
|
25
|
+
This is on purpose. WebAssembly aims to be **deterministic**: given the same inputs, the same code produces the same outputs, every time. That makes it safe to run untrusted code, easy to cache, and easy to reason about. But the current time is not deterministic: it is different every time you ask. So a WebAssembly module cannot just "know" the time; it has to **ask the host** for it. Reading the clock is a call across the sandbox boundary.
|
|
26
|
+
|
|
27
|
+
`Time` is the thin, friendly wrapper over that call. Under the hood it uses the host's `Date.now()` binding (`env.Date.now`).
|
|
28
|
+
|
|
29
|
+
```mermaid
|
|
30
|
+
flowchart LR
|
|
31
|
+
subgraph Sandbox["Your backend (WebAssembly sandbox)"]
|
|
32
|
+
C["Time.nowMillis()"]
|
|
33
|
+
end
|
|
34
|
+
subgraph Host["Dacely edge host"]
|
|
35
|
+
D["env.Date.now"]
|
|
36
|
+
K["real system clock"]
|
|
37
|
+
end
|
|
38
|
+
C -->|host import| D
|
|
39
|
+
D --> K
|
|
40
|
+
K -->|milliseconds| C
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Both the edge and the dev server provide this binding, so time behaves **identically** in `toiljs dev` and in production.
|
|
44
|
+
|
|
45
|
+
## `Time` vs calling `Date.now()`
|
|
46
|
+
|
|
47
|
+
ToilScript's `Date.now()` lowers to the exact same host import, so you *can* call it directly. Prefer `Time`:
|
|
48
|
+
|
|
49
|
+
- It makes the host boundary (and the single millisecond unit) explicit and easy to find in a codebase.
|
|
50
|
+
- It gives you `nowSeconds()` without writing an open-coded `/ 1000` at every call site.
|
|
51
|
+
|
|
52
|
+
Both are the same clock, so timing you do in a handler lines up with timing the framework does. For example, the auth system uses `Time.nowSeconds()` for session issue and expiry times.
|
|
53
|
+
|
|
54
|
+
## Correct usage: timestamps and TTLs
|
|
55
|
+
|
|
56
|
+
`Time` is **wall-clock time**, exactly like a browser's `Date.now()`. It follows the system clock, which means it can occasionally **step backward** (for instance when the machine corrects its clock against a time server).
|
|
57
|
+
|
|
58
|
+
Do:
|
|
59
|
+
|
|
60
|
+
- **Stamp an instant.** Record when something happened, so you can compare it later: an event time, a "last seen" marker.
|
|
61
|
+
- **Compute an expiry (a TTL).** A **TTL** ("time to live") is a deadline. You set it by adding a duration to *now*, and you check it by comparing *now* against the stored deadline:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const ttlSeconds: u64 = 3600; // valid for one hour
|
|
65
|
+
const expiresAt = Time.nowSeconds() + ttlSeconds;
|
|
66
|
+
|
|
67
|
+
// ...later, on another request...
|
|
68
|
+
const expired = Time.nowSeconds() >= expiresAt;
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Do not:
|
|
72
|
+
|
|
73
|
+
- **Measure elapsed time with it.** Do not use it as a stopwatch to time how long an operation took. Because the wall clock can step backward, `end - start` could come out zero or negative. `Time` answers "what instant is it now?", not "how much time has passed?".
|
|
74
|
+
|
|
75
|
+
## Gotchas
|
|
76
|
+
|
|
77
|
+
- **It is wall-clock, not monotonic.** It can jump backward across a clock correction. Fine for timestamps and expiries; wrong for measuring durations.
|
|
78
|
+
- **Milliseconds vs seconds.** `nowMillis()` and `nowSeconds()` differ by a factor of 1000. Mixing them (comparing a millisecond stamp to a second stamp) is a common bug. Sessions and login challenges use **seconds**.
|
|
79
|
+
- **UTC only.** These are epoch counts, with no time zone. Format for display on the client if you need a local time.
|
|
80
|
+
|
|
81
|
+
## Related
|
|
82
|
+
|
|
83
|
+
- [Auth, sessions, and `@user`](../auth/usage.md): sessions use `Time.nowSeconds()` for their issue and expiry stamps.
|
|
84
|
+
- [Caching](./caching.md): TTLs are the same idea, a deadline after which a saved value is stale.
|
|
85
|
+
- [Compute tiers](../concepts/tiers.md): more on the WebAssembly sandbox your backend runs in.
|
|
@@ -25,8 +25,10 @@ const METRICS = [
|
|
|
25
25
|
{ id: 13, label: 'DB ops', unit: 'count' },
|
|
26
26
|
{ id: 26, label: 'Stream bytes out', unit: 'bytes' },
|
|
27
27
|
{ id: 39, label: 'Memory bandwidth', unit: 'bytes' },
|
|
28
|
-
{ id: 41, label: '
|
|
29
|
-
{ id:
|
|
28
|
+
{ id: 41, label: 'Cache hits', unit: 'count' },
|
|
29
|
+
{ id: 42, label: 'Cache misses', unit: 'count' },
|
|
30
|
+
{ id: 43, label: 'Connected streams', unit: 'count' },
|
|
31
|
+
{ id: 45, label: 'Committed memory', unit: 'bytes' }
|
|
30
32
|
] as const;
|
|
31
33
|
|
|
32
34
|
// AnalyticsRange (mirrors the server enum).
|
|
@@ -157,20 +159,19 @@ export default function AnalyticsDemo() {
|
|
|
157
159
|
|
|
158
160
|
<section style={{ marginTop: 24 }}>
|
|
159
161
|
<h2>Snapshot</h2>
|
|
162
|
+
<p style={{ color: '#64748b', marginTop: 0 }}>
|
|
163
|
+
The <code>live</code> gauges are the current level right now, and the windows are the
|
|
164
|
+
current rate-limit usage vs the plan cap (cap ∞ = unlimited). To see a metric over time,
|
|
165
|
+
use the chart above (the time-series), not an ever-growing lifetime total.
|
|
166
|
+
</p>
|
|
160
167
|
<button onClick={loadStats}>Load my site analytics</button>
|
|
161
168
|
{err && <p style={{ color: '#f87171' }}>{err}</p>}
|
|
162
169
|
{stats && (
|
|
163
170
|
<ul>
|
|
164
|
-
<li>
|
|
165
|
-
<li>
|
|
166
|
-
<li>
|
|
167
|
-
<li>
|
|
168
|
-
<li>2xx responses: {String(stats.status2xx)}</li>
|
|
169
|
-
<li>db ops: {String(stats.dbOps)}</li>
|
|
170
|
-
<li>connected streams (live): {String(stats.connectedStreams)}</li>
|
|
171
|
-
<li>committed memory (live): {fmt(Number(stats.committedMemory), 'bytes')}</li>
|
|
172
|
-
<li>requests this minute: {cap(stats.reqMinuteUsed, stats.reqMinuteCap)}</li>
|
|
173
|
-
<li>requests today: {cap(stats.reqDayUsed, stats.reqDayCap)}</li>
|
|
171
|
+
<li>connected streams (live): {String(stats.liveConnectedStreams)}</li>
|
|
172
|
+
<li>committed memory (live): {fmt(Number(stats.liveCommittedMemoryBytes), 'bytes')}</li>
|
|
173
|
+
<li>requests this minute: {cap(stats.requestsThisMinute, stats.requestsThisMinuteCap)}</li>
|
|
174
|
+
<li>requests today: {cap(stats.requestsToday, stats.requestsTodayCap)}</li>
|
|
174
175
|
</ul>
|
|
175
176
|
)}
|
|
176
177
|
</section>
|
|
@@ -1,25 +1,37 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* This site's own analytics snapshot, mapped from the toilscript `Analytics.self()` frame.
|
|
3
|
+
*
|
|
4
|
+
* This example deliberately returns only CURRENT state: the `live*` gauges (the instantaneous level
|
|
5
|
+
* right now) and the request-window counters (usage in the current 1-minute and 24-hour rate-limit
|
|
6
|
+
* windows, paired with the plan cap, where cap `0` = unlimited). It does NOT surface lifetime
|
|
7
|
+
* cumulative `total*` counters. For historical, time-windowed values use the separate `SeriesData`
|
|
8
|
+
* time-series (`/series`), which reads per-bucket history rather than an ever-growing running total.
|
|
9
|
+
*
|
|
10
|
+
* All fields are `u64` (the guest exposes `u64` getters, so the mapping needs zero casts).
|
|
11
|
+
*/
|
|
3
12
|
@data
|
|
4
13
|
export class SiteAnalytics {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
// --- Live gauges (current instantaneous level, right now, NOT a total) ---
|
|
15
|
+
/** Streams currently connected right now. */
|
|
16
|
+
liveConnectedStreams: u64 = 0;
|
|
17
|
+
/** Wasm linear memory currently committed right now, in bytes. */
|
|
18
|
+
liveCommittedMemoryBytes: u64 = 0;
|
|
19
|
+
|
|
20
|
+
// --- Request windows: current rate-limit usage vs plan cap (cap 0 = unlimited) ---
|
|
21
|
+
/** Requests used in the current 1-minute window. */
|
|
22
|
+
requestsThisMinute: u64 = 0;
|
|
23
|
+
/** Plan cap for the 1-minute window (0 = unlimited). */
|
|
24
|
+
requestsThisMinuteCap: u64 = 0;
|
|
25
|
+
/** Requests used in the current 24-hour window. */
|
|
26
|
+
requestsToday: u64 = 0;
|
|
27
|
+
/** Plan cap for the 24-hour window (0 = unlimited). */
|
|
28
|
+
requestsTodayCap: u64 = 0;
|
|
18
29
|
}
|
|
19
30
|
|
|
20
|
-
/** One metric's historical
|
|
31
|
+
/** One metric's historical TIME-SERIES for a range, from `Analytics.series(metric, range)`: the raw
|
|
21
32
|
* per-bucket totals (oldest→newest) plus the bucket width + newest-bucket end, so the client can draw a
|
|
22
|
-
* graph and derive per-second rates.
|
|
33
|
+
* graph and derive per-second rates. This is per-bucket HISTORY: the right way to see a metric over time,
|
|
34
|
+
* instead of an ever-growing lifetime counter. Demonstrates the typed series API. */
|
|
23
35
|
@data
|
|
24
36
|
export class SeriesData {
|
|
25
37
|
metric: i32 = 0;
|
|
@@ -16,23 +16,21 @@ import { SeriesData, SiteAnalytics } from '../models/SiteAnalytics';
|
|
|
16
16
|
class AnalyticsRoutes {
|
|
17
17
|
@get('/')
|
|
18
18
|
public self(): SiteAnalytics {
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
r.reqDayCap = <i64>s.reqDayCap;
|
|
35
|
-
return r;
|
|
19
|
+
const stats = Analytics.self();
|
|
20
|
+
const out = new SiteAnalytics();
|
|
21
|
+
|
|
22
|
+
// Live gauges (current instantaneous level, not a cumulative total).
|
|
23
|
+
out.liveConnectedStreams = stats.connectedStreams;
|
|
24
|
+
out.liveCommittedMemoryBytes = stats.committedMemory;
|
|
25
|
+
|
|
26
|
+
// Request windows: current usage vs plan cap (cap 0 = unlimited).
|
|
27
|
+
out.requestsThisMinute = stats.reqMinuteUsed;
|
|
28
|
+
out.requestsThisMinuteCap = stats.reqMinuteCap;
|
|
29
|
+
out.requestsToday = stats.reqDayUsed;
|
|
30
|
+
out.requestsTodayCap = stats.reqDayCap;
|
|
31
|
+
|
|
32
|
+
// For historical, time-windowed values (per-bucket, not a lifetime total), see `/series` below.
|
|
33
|
+
return out;
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
/// Any metric's historical time-series for a range, for graphs:
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Response } from 'toiljs/server/runtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Demo of `ToilUserId` — the stable 256-bit user identity derived from the ML-DSA login public key +
|
|
5
|
+
* identifier (email/username) + tenant domain. Deterministic, opaque, O(1) to compare with `==` / `!=`.
|
|
6
|
+
*/
|
|
7
|
+
@rest('userid')
|
|
8
|
+
class UserIdDemo {
|
|
9
|
+
@get('/demo')
|
|
10
|
+
public demo(): Response {
|
|
11
|
+
const key = new Uint8Array(1312); // an ML-DSA-44 public key (zeros for the demo)
|
|
12
|
+
const a = ToilUserId.derive(key, 'alice@example.com', 'acme.dacely.com');
|
|
13
|
+
const b = ToilUserId.derive(key, 'alice@example.com', 'acme.dacely.com');
|
|
14
|
+
const c = ToilUserId.derive(key, 'bob@example.com', 'acme.dacely.com');
|
|
15
|
+
// Same inputs => same id (a == b); a different identifier => a different id (a != c).
|
|
16
|
+
const same = a == b;
|
|
17
|
+
const diff = a != c;
|
|
18
|
+
return Response.text(
|
|
19
|
+
a.toHex() + '\nsame=' + same.toString() + ' diff=' + diff.toString() + '\n',
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toiljs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.87",
|
|
5
5
|
"author": "Dacely",
|
|
6
6
|
"description": "The modern React framework: a file-based React frontend and a ToilScript-compiled WebAssembly backend.",
|
|
7
7
|
"repository": {
|
|
@@ -67,6 +67,10 @@
|
|
|
67
67
|
"types": "./server/runtime/*.ts",
|
|
68
68
|
"default": "./server/runtime/*.ts"
|
|
69
69
|
},
|
|
70
|
+
"./server/auth": {
|
|
71
|
+
"types": "./server/auth/index.ts",
|
|
72
|
+
"default": "./server/auth/index.ts"
|
|
73
|
+
},
|
|
70
74
|
"./tsconfig": "./presets/tsconfig.json",
|
|
71
75
|
"./eslint": "./presets/eslint.js",
|
|
72
76
|
"./prettier": "./presets/prettier.json",
|
|
@@ -120,21 +124,21 @@
|
|
|
120
124
|
"@dacely/hyper-express": "6.17.4",
|
|
121
125
|
"@dacely/noble-post-quantum": "^0.6.1",
|
|
122
126
|
"@dacely/toilscript-loader": "^0.1.0",
|
|
123
|
-
"@eslint-react/eslint-plugin": "^5.
|
|
127
|
+
"@eslint-react/eslint-plugin": "^5.10.1",
|
|
124
128
|
"@eslint/js": "^10.0.1",
|
|
125
129
|
"@noble/curves": "^2.2.0",
|
|
126
|
-
"@typescript-eslint/utils": "^8.62.
|
|
130
|
+
"@typescript-eslint/utils": "^8.62.1",
|
|
127
131
|
"@vitejs/plugin-react": "^6.0.3",
|
|
128
132
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
129
133
|
"eslint-plugin-react-refresh": "^0.5.3",
|
|
130
134
|
"hash-wasm": "^4.12.0",
|
|
131
135
|
"juice": "^12.1.1",
|
|
132
|
-
"nodemailer": "^9.0.
|
|
136
|
+
"nodemailer": "^9.0.3",
|
|
133
137
|
"picocolors": "^1.1.1",
|
|
134
|
-
"sharp": "^0.35.
|
|
135
|
-
"toilscript": "^0.1.
|
|
136
|
-
"typescript-eslint": "^8.62.
|
|
137
|
-
"vite": "^8.1.
|
|
138
|
+
"sharp": "^0.35.3",
|
|
139
|
+
"toilscript": "^0.1.52",
|
|
140
|
+
"typescript-eslint": "^8.62.1",
|
|
141
|
+
"vite": "^8.1.3",
|
|
138
142
|
"vite-imagetools": "^10.0.1",
|
|
139
143
|
"vite-plugin-node-polyfills": "^0.28.0"
|
|
140
144
|
},
|
|
@@ -157,17 +161,17 @@
|
|
|
157
161
|
"@microsoft/api-extractor": "7.58.9",
|
|
158
162
|
"@testing-library/dom": "^10.4.1",
|
|
159
163
|
"@testing-library/react": "^16.3.2",
|
|
160
|
-
"@types/node": "^26.
|
|
164
|
+
"@types/node": "^26.1.0",
|
|
161
165
|
"@types/nodemailer": "^8.0.1",
|
|
162
166
|
"@types/react": "^19.2.17",
|
|
163
167
|
"@types/react-dom": "^19.2.3",
|
|
164
168
|
"@vitest/coverage-v8": "^4.1.9",
|
|
165
169
|
"@vitest/ui": "^4.1.9",
|
|
166
170
|
"esbuild": "^0.28.1",
|
|
167
|
-
"eslint": "^10.
|
|
171
|
+
"eslint": "^10.6.0",
|
|
168
172
|
"jsdom": "^29.1.1",
|
|
169
173
|
"micromatch": "^4.0.8",
|
|
170
|
-
"prettier": "^3.
|
|
174
|
+
"prettier": "^3.9.4",
|
|
171
175
|
"react": "^19.2.7",
|
|
172
176
|
"react-dom": "^19.2.7",
|
|
173
177
|
"typedoc": "^0.28.19",
|
|
@@ -23,48 +23,37 @@ const docsDir = path.join(repoRoot, 'docs');
|
|
|
23
23
|
const outFile = path.join(repoRoot, 'src', 'compiler', 'toil-docs.generated.ts');
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* Guides excluded from the AGENT doc set (still published as human docs)
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* Guides excluded from the AGENT doc set (still published as human docs).
|
|
27
|
+
* Nothing is excluded today: every folder's `README.md` landing page and its
|
|
28
|
+
* topic pages are bundled so the agent gets the full navigation.
|
|
29
29
|
*/
|
|
30
|
-
const EXCLUDE = new Set(
|
|
30
|
+
const EXCLUDE = new Set();
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
* Stable order for the agent docs
|
|
34
|
-
*
|
|
33
|
+
* Stable order for the agent docs: the root landing page first, then every
|
|
34
|
+
* other guide alphabetically by its repo-relative path (which naturally groups
|
|
35
|
+
* each folder together, led by its `README.md` landing). Adding a new doc file
|
|
36
|
+
* is picked up automatically.
|
|
35
37
|
*/
|
|
36
|
-
const ORDER = [
|
|
37
|
-
'index.md',
|
|
38
|
-
'getting-started.md',
|
|
39
|
-
'routing.md',
|
|
40
|
-
'client.md',
|
|
41
|
-
'styling.md',
|
|
42
|
-
'server.md',
|
|
43
|
-
'ssr.md',
|
|
44
|
-
'rpc.md',
|
|
45
|
-
'tiers.md',
|
|
46
|
-
'streams.md',
|
|
47
|
-
'daemon.md',
|
|
48
|
-
'data.md',
|
|
49
|
-
'caching.md',
|
|
50
|
-
'ratelimit.md',
|
|
51
|
-
'auth.md',
|
|
52
|
-
'environment.md',
|
|
53
|
-
'email.md',
|
|
54
|
-
'cookies.md',
|
|
55
|
-
'crypto.md',
|
|
56
|
-
'time.md',
|
|
57
|
-
'cli.md',
|
|
58
|
-
];
|
|
38
|
+
const ORDER = ['README.md'];
|
|
59
39
|
|
|
60
40
|
function collect() {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
41
|
+
// Recurse one+ levels so a topic FOLDER (e.g. `docs/auth/*.md`) is bundled too, keyed by its
|
|
42
|
+
// repo-relative path (`auth/index.md`). Adding a `docs/**/*.md` is picked up automatically.
|
|
43
|
+
const files = [];
|
|
44
|
+
const walk = (dir, prefix) => {
|
|
45
|
+
for (const ent of fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
46
|
+
if (ent.isDirectory()) {
|
|
47
|
+
walk(path.join(dir, ent.name), prefix + ent.name + '/');
|
|
48
|
+
} else if (ent.name.endsWith('.md') && !EXCLUDE.has(prefix + ent.name) && !EXCLUDE.has(ent.name)) {
|
|
49
|
+
files.push(prefix + ent.name);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
walk(docsDir, '');
|
|
65
54
|
const ordered = [
|
|
66
|
-
...ORDER.filter((f) =>
|
|
67
|
-
...
|
|
55
|
+
...ORDER.filter((f) => files.includes(f)),
|
|
56
|
+
...files.filter((f) => !ORDER.includes(f)),
|
|
68
57
|
];
|
|
69
58
|
const out = {};
|
|
70
59
|
for (const name of ordered) {
|