toiljs 0.0.84 → 0.0.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/build/cli/.tsbuildinfo +1 -1
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/config.d.ts +2 -0
- package/build/compiler/config.js +1 -0
- package/build/compiler/generate.js +3 -2
- package/build/compiler/index.d.ts +1 -1
- package/build/compiler/index.js +44 -5
- package/build/compiler/toil-docs.generated.js +6 -1
- package/build/devserver/.tsbuildinfo +1 -1
- package/build/devserver/analytics/index.js +113 -33
- package/build/devserver/runtime/module.js +1 -0
- package/docs/auth/configuration.md +94 -0
- package/docs/auth/extending.md +170 -0
- package/docs/auth/how-it-works.md +138 -0
- package/docs/auth/index.md +102 -0
- package/docs/auth/usage.md +188 -0
- package/docs/auth.md +6 -0
- package/examples/basic/client/routes/analytics.tsx +164 -26
- package/examples/basic/server/models/SiteAnalytics.ts +139 -11
- package/examples/basic/server/routes/Analytics.ts +89 -13
- package/examples/basic/server/routes/UserId.ts +22 -0
- package/package.json +5 -1
- package/scripts/gen-toil-docs.mjs +15 -6
- package/server/auth/AuthController.ts +335 -0
- package/server/auth/AuthUser.ts +49 -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/generate.ts +3 -2
- package/src/compiler/index.ts +74 -5
- package/src/compiler/toil-docs.generated.ts +6 -1
- package/src/devserver/analytics/index.ts +139 -38
- package/src/devserver/runtime/module.ts +1 -0
- package/test/analytics-dev.test.ts +76 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ToilUserId` — the stable, tenant-scoped 256-bit user identity.
|
|
3
|
+
*
|
|
4
|
+
* Derived once as `sha256(mldsaPublicKey || identifier || domain)`, so the SAME user (same ML-DSA login
|
|
5
|
+
* public key + the same email/username) on the SAME tenant domain always maps to the SAME id — independent
|
|
6
|
+
* of session, cookie, or device. It is opaque and one-way (a hash), so it is safe to store, log, or use as
|
|
7
|
+
* a stable database key without leaking the underlying key or address.
|
|
8
|
+
*
|
|
9
|
+
* Backed by four 64-bit words (a 256-bit value), NOT a byte array, so equality is four word compares with
|
|
10
|
+
* early-out — no allocation, no byte loop. `==` / `!=` are overloaded to value equality, and `equals()` is
|
|
11
|
+
* the explicit form. (`===` in AssemblyScript is reference identity and is NOT overloadable; use `==` for
|
|
12
|
+
* value equality.)
|
|
13
|
+
*
|
|
14
|
+
* A global (no import), like `crypto` / `AuthService`. `AuthService` mints the current user's id from the
|
|
15
|
+
* login key + identifier + the request's tenant domain, so `@auth` handlers can key on a stable user.
|
|
16
|
+
*/
|
|
17
|
+
export class ToilUserId {
|
|
18
|
+
constructor(
|
|
19
|
+
public w0: u64 = 0,
|
|
20
|
+
public w1: u64 = 0,
|
|
21
|
+
public w2: u64 = 0,
|
|
22
|
+
public w3: u64 = 0,
|
|
23
|
+
) {}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Derive the id from the user's ML-DSA public key, their identifier (email or username), and the
|
|
27
|
+
* tenant `domain` (the site on dacely). Deterministic: identical inputs → identical id. The three
|
|
28
|
+
* inputs are concatenated in order and SHA-256'd; length-framing is unnecessary because the ML-DSA
|
|
29
|
+
* public key is a FIXED length (1312 bytes for ML-DSA-44), so the boundary before `identifier` is
|
|
30
|
+
* unambiguous, and `domain` is the trusted tail supplied by the host, not the user.
|
|
31
|
+
*/
|
|
32
|
+
static derive(mldsaPublicKey: Uint8Array, identifier: string, domain: string): ToilUserId {
|
|
33
|
+
const id = Uint8Array.wrap(String.UTF8.encode(identifier));
|
|
34
|
+
const dom = Uint8Array.wrap(String.UTF8.encode(domain));
|
|
35
|
+
const buf = new Uint8Array(mldsaPublicKey.length + id.length + dom.length);
|
|
36
|
+
buf.set(mldsaPublicKey, 0);
|
|
37
|
+
buf.set(id, mldsaPublicKey.length);
|
|
38
|
+
buf.set(dom, mldsaPublicKey.length + id.length);
|
|
39
|
+
return ToilUserId.fromBytes(crypto.sha256(buf));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Build from a 32-byte digest. Bytes past 32 are ignored; a shorter array zero-pads the tail. */
|
|
43
|
+
static fromBytes(b: Uint8Array): ToilUserId {
|
|
44
|
+
if (b.length >= 32) {
|
|
45
|
+
const p = b.dataStart;
|
|
46
|
+
return new ToilUserId(load<u64>(p, 0), load<u64>(p, 8), load<u64>(p, 16), load<u64>(p, 24));
|
|
47
|
+
}
|
|
48
|
+
const tmp = new Uint8Array(32);
|
|
49
|
+
tmp.set(b, 0);
|
|
50
|
+
const p = tmp.dataStart;
|
|
51
|
+
return new ToilUserId(load<u64>(p, 0), load<u64>(p, 8), load<u64>(p, 16), load<u64>(p, 24));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The 32-byte identity (the original SHA-256 digest bytes; wasm is little-endian, so this round-trips). */
|
|
55
|
+
toBytes(): Uint8Array {
|
|
56
|
+
const out = new Uint8Array(32);
|
|
57
|
+
const p = out.dataStart;
|
|
58
|
+
store<u64>(p, this.w0, 0);
|
|
59
|
+
store<u64>(p, this.w1, 8);
|
|
60
|
+
store<u64>(p, this.w2, 16);
|
|
61
|
+
store<u64>(p, this.w3, 24);
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Lowercase hex, 64 chars. */
|
|
66
|
+
toHex(): string {
|
|
67
|
+
const b = this.toBytes();
|
|
68
|
+
const HEX = '0123456789abcdef';
|
|
69
|
+
let s = '';
|
|
70
|
+
for (let i = 0; i < 32; i++) {
|
|
71
|
+
const v = <i32>b[i];
|
|
72
|
+
s += HEX.charAt(v >> 4);
|
|
73
|
+
s += HEX.charAt(v & 0xf);
|
|
74
|
+
}
|
|
75
|
+
return s;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** True when unset (the all-zero id, e.g. an absent / anonymous user). */
|
|
79
|
+
isZero(): bool {
|
|
80
|
+
return (this.w0 | this.w1 | this.w2 | this.w3) == 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Value equality: four u64 compares, short-circuiting. O(1), no allocation. */
|
|
84
|
+
@inline
|
|
85
|
+
equals(other: ToilUserId): bool {
|
|
86
|
+
return (
|
|
87
|
+
this.w0 == other.w0 &&
|
|
88
|
+
this.w1 == other.w1 &&
|
|
89
|
+
this.w2 == other.w2 &&
|
|
90
|
+
this.w3 == other.w3
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** `a == b` value equality (overloaded). */
|
|
95
|
+
@operator('==')
|
|
96
|
+
@inline
|
|
97
|
+
eq(other: ToilUserId): bool {
|
|
98
|
+
return this.equals(other);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** `a != b` value inequality (overloaded). */
|
|
102
|
+
@operator('!=')
|
|
103
|
+
@inline
|
|
104
|
+
ne(other: ToilUserId): bool {
|
|
105
|
+
return !this.equals(other);
|
|
106
|
+
}
|
|
107
|
+
}
|
package/src/compiler/config.ts
CHANGED
|
@@ -140,6 +140,16 @@ export interface ServerConfig {
|
|
|
140
140
|
readonly email?: EmailBackendConfig;
|
|
141
141
|
/** Which layer the dev process emulates. Default `all`. */
|
|
142
142
|
readonly nodeMode?: DevNodeMode;
|
|
143
|
+
/**
|
|
144
|
+
* Built-in auth. `true` opts into the framework's post-quantum login: the
|
|
145
|
+
* build appends a shipped `@rest('auth')` controller + its `@user` shape to
|
|
146
|
+
* the toilscript entry set, so the app gets the full `/auth/register|login`
|
|
147
|
+
* API + sessions (`/auth/me`, `/auth/logout`) with no hand-written boilerplate.
|
|
148
|
+
* Default `false`. (The escape hatch `import 'toiljs/server/auth'` in
|
|
149
|
+
* `server/main.ts` does the same without this flag.) An app that opts in must
|
|
150
|
+
* NOT declare its own `@user` — the built-in owns the single per-program one.
|
|
151
|
+
*/
|
|
152
|
+
readonly auth?: boolean;
|
|
143
153
|
/** Daemon (L4) config mirror (dev / self-host). */
|
|
144
154
|
readonly daemon?: DaemonConfig;
|
|
145
155
|
/**
|
|
@@ -202,6 +212,8 @@ export interface ResolvedToilConfig {
|
|
|
202
212
|
readonly email: EmailBackendConfig | null;
|
|
203
213
|
/** Which layer the dev process emulates (dev / self-host). Default `all`. */
|
|
204
214
|
readonly nodeMode: DevNodeMode;
|
|
215
|
+
/** Whether the built-in `/auth/*` PQ-login controller is compiled + mounted. */
|
|
216
|
+
readonly auth: boolean;
|
|
205
217
|
/** Daemon (L4) config mirror (dev / self-host), every field resolved. */
|
|
206
218
|
readonly daemon: ResolvedDaemonConfig;
|
|
207
219
|
/** Self-host HTTP worker count for `toiljs start`. */
|
|
@@ -279,6 +291,7 @@ export async function loadConfig(
|
|
|
279
291
|
seo: client.seo ?? null,
|
|
280
292
|
email: user.server?.email ?? null,
|
|
281
293
|
nodeMode: resolveNodeMode(user.server?.nodeMode),
|
|
294
|
+
auth: user.server?.auth === true,
|
|
282
295
|
daemon: resolveDaemonConfig(user.server?.daemon),
|
|
283
296
|
threads: resolveThreads(user.server?.threads),
|
|
284
297
|
runtimePath: resolveRuntimePath(),
|
package/src/compiler/generate.ts
CHANGED
|
@@ -125,8 +125,9 @@ export const TOIL_SERVER_ENV_DTS =
|
|
|
125
125
|
`declare class TwoFactorIssue { code: string; token: string; constructor(code: string, token: string); }\n` +
|
|
126
126
|
`declare class TwoFactorChallenge { token: string; status: EmailStatus; constructor(token: string, status: EmailStatus); }\n` +
|
|
127
127
|
`declare namespace TwoFactor { const DEFAULT_TTL_SECS: u64; const DEFAULT_DIGITS: i32; function setSecret(secret: Uint8Array): void; function issue(recipient: string, purpose: string, ttlSecs?: u64, digits?: i32): TwoFactorIssue; function send(recipient: string, purpose: string, ttlSecs?: u64, digits?: i32): TwoFactorChallenge; function verify(token: string, recipient: string, code: string): bool; }\n` +
|
|
128
|
-
`declare namespace AuthService { const SESSION_COOKIE: string; const USER_COOKIE: string; const LOGIN_CONTEXT: string; const PUBLIC_KEY_LEN: i32; const SIGNATURE_LEN: i32; const DEFAULT_SESSION_TTL_SECS: u64; function setSecret(secret: Uint8Array): void; function hasSession(): bool; function getSessionBytes(): Uint8Array | null; function getUser(): __ToilAuthUser | null; function mintSession(userData: Uint8Array, ttlSecs?: u64): Cookie; function clearSession(): Cookie; function userCookie(userData: Uint8Array, ttlSecs?: u64): Cookie; function clearUserCookie(): Cookie; function buildLoginMessage(sub: string, aud: string, cid: Uint8Array, nonce: Uint8Array, iat: u64, exp: u64, ciphertext: Uint8Array, memKiB: u32, iterations: u32, parallelism: u32, serverKemKeyId: Uint8Array): Uint8Array; function verifyLogin(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): bool; const KEM_CIPHERTEXT_LEN: i32; const KEM_SECRET_KEY_LEN: i32; const KEM_PUBLIC_KEY_LEN: i32; const SHARED_SECRET_LEN: i32; const OPRF_ELEMENT_LEN: i32; const OPRF_SEED_LEN: i32; const SESSION_KEY_LABEL: string; const SERVER_CONFIRM_LABEL: string; function setOprfSeed(seed: Uint8Array): void; function setServerKemSecretKey(secretKey: Uint8Array): void; function setServerKemPublicKey(publicKey: Uint8Array): void; function serverKemKeyId(): Uint8Array; function oprfEvaluate(username: string, blinded: Uint8Array): Uint8Array; function mlkemDecapsulate(ciphertext: Uint8Array): Uint8Array; function sha256(data: Uint8Array): Uint8Array; function deriveSessionKey(sharedSecret: Uint8Array, transcriptHash: Uint8Array): Uint8Array; function serverConfirmTag(sessionKey: Uint8Array, transcriptHash: Uint8Array): Uint8Array; const REGISTER_CONTEXT: string; function buildRegisterMessage(username: string, publicKey: Uint8Array): Uint8Array; function verifyRegister(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): bool; }\n` +
|
|
129
|
-
`interface __ToilAuthUser {}\n
|
|
128
|
+
`declare namespace AuthService { const SESSION_COOKIE: string; const USER_COOKIE: string; const LOGIN_CONTEXT: string; const PUBLIC_KEY_LEN: i32; const SIGNATURE_LEN: i32; const DEFAULT_SESSION_TTL_SECS: u64; function setSecret(secret: Uint8Array): void; function hasSession(): bool; function getSessionBytes(): Uint8Array | null; function getUser(): __ToilAuthUser | null; function userId(): ToilUserId | null; function enable(): void; function mintSession(userData: Uint8Array, ttlSecs?: u64): Cookie; function clearSession(): Cookie; function userCookie(userData: Uint8Array, ttlSecs?: u64): Cookie; function clearUserCookie(): Cookie; function buildLoginMessage(sub: string, aud: string, cid: Uint8Array, nonce: Uint8Array, iat: u64, exp: u64, ciphertext: Uint8Array, memKiB: u32, iterations: u32, parallelism: u32, serverKemKeyId: Uint8Array): Uint8Array; function verifyLogin(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): bool; const KEM_CIPHERTEXT_LEN: i32; const KEM_SECRET_KEY_LEN: i32; const KEM_PUBLIC_KEY_LEN: i32; const SHARED_SECRET_LEN: i32; const OPRF_ELEMENT_LEN: i32; const OPRF_SEED_LEN: i32; const SESSION_KEY_LABEL: string; const SERVER_CONFIRM_LABEL: string; function setOprfSeed(seed: Uint8Array): void; function setServerKemSecretKey(secretKey: Uint8Array): void; function setServerKemPublicKey(publicKey: Uint8Array): void; function serverKemKeyId(): Uint8Array; function oprfEvaluate(username: string, blinded: Uint8Array): Uint8Array; function mlkemDecapsulate(ciphertext: Uint8Array): Uint8Array; function sha256(data: Uint8Array): Uint8Array; function deriveSessionKey(sharedSecret: Uint8Array, transcriptHash: Uint8Array): Uint8Array; function serverConfirmTag(sessionKey: Uint8Array, transcriptHash: Uint8Array): Uint8Array; const REGISTER_CONTEXT: string; function buildRegisterMessage(username: string, publicKey: Uint8Array): Uint8Array; function verifyRegister(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): bool; }\n` +
|
|
129
|
+
`interface __ToilAuthUser {}\n` +
|
|
130
|
+
`declare class ToilUserId { w0: u64; w1: u64; w2: u64; w3: u64; constructor(w0?: u64, w1?: u64, w2?: u64, w3?: u64); static derive(mldsaPublicKey: Uint8Array, identifier: string, domain: string): ToilUserId; static fromBytes(b: Uint8Array): ToilUserId; toBytes(): Uint8Array; toHex(): string; isZero(): bool; equals(other: ToilUserId): bool; }\n`;
|
|
130
131
|
|
|
131
132
|
/**
|
|
132
133
|
* Returns a `./`-prefixed, **extensionless** POSIX module specifier from `.toil` to `abs`, for use
|
package/src/compiler/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import fs from 'node:fs';
|
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
import net from 'node:net';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
6
7
|
|
|
7
8
|
import pc from 'picocolors';
|
|
8
9
|
import { build as viteBuild, createServer, mergeConfig, type ViteDevServer } from 'vite';
|
|
@@ -106,6 +107,56 @@ function serverEntryFiles(root: string): string[] {
|
|
|
106
107
|
return [...result].sort();
|
|
107
108
|
}
|
|
108
109
|
|
|
110
|
+
/**
|
|
111
|
+
* The framework-shipped built-in-auth entry files (root-relative), appended to the toilscript entry
|
|
112
|
+
* set when `server.auth` is on. `AuthUser.ts` (the `@user` shape) is FIRST because `AuthController.ts`
|
|
113
|
+
* (the `@rest('auth')` controller) imports it. Primary lookup is the conventional install location
|
|
114
|
+
* under the app's `node_modules/toiljs` (which transparently follows a symlinked/linked toiljs); the
|
|
115
|
+
* fallback resolves the running toiljs package (`…/build/compiler/index.js` -> package root) for a
|
|
116
|
+
* hoisted install. Throws a clear error if the shipped files are missing.
|
|
117
|
+
*/
|
|
118
|
+
function authEntryFiles(root: string): string[] {
|
|
119
|
+
const names = ['AuthUser.ts', 'AuthController.ts'];
|
|
120
|
+
const primary = names.map((n) => path.posix.join('node_modules/toiljs/server/auth', n));
|
|
121
|
+
if (primary.every((rel) => fs.existsSync(path.join(root, rel)))) return primary;
|
|
122
|
+
// Fallback: locate this running toiljs package dir and make the paths root-relative.
|
|
123
|
+
try {
|
|
124
|
+
const pkgDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
125
|
+
const abs = names.map((n) => path.join(pkgDir, 'server', 'auth', n));
|
|
126
|
+
if (abs.every((p) => fs.existsSync(p)))
|
|
127
|
+
return abs.map((p) => path.relative(root, p).replace(/\\/g, '/'));
|
|
128
|
+
} catch {
|
|
129
|
+
// fall through to the error below
|
|
130
|
+
}
|
|
131
|
+
throw new Error(
|
|
132
|
+
'toiljs: server.auth is enabled but the built-in auth controller ' +
|
|
133
|
+
'(server/auth/AuthController.ts) was not found under node_modules/toiljs. ' +
|
|
134
|
+
'Reinstall toiljs (npm i toiljs), or remove server.auth from toil.config.ts.',
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Whether any server entry declares the escape-hatch `import 'toiljs/server/auth'` (a bare side-effect
|
|
140
|
+
* import) — the lighter opt-in that turns on the built-in auth surface without the `server.auth` config
|
|
141
|
+
* flag. Framework-shipped decorator sources under node_modules only WEAVE as explicit toilscript entries
|
|
142
|
+
* (a transitive import lands them under the `~lib/` LIBRARY prefix, where `@data`/`@rest`/`@user` do not
|
|
143
|
+
* weave), so the bare import is only a SIGNAL: it triggers the same entry injection as the flag. `import
|
|
144
|
+
* type` is skipped (erased). The marker module `server/auth/index.ts` is intentionally empty so it never
|
|
145
|
+
* pulls a conflicting `~lib/` copy of the controller into the compile.
|
|
146
|
+
*/
|
|
147
|
+
function serverImportsAuth(root: string, files: string[]): boolean {
|
|
148
|
+
// Bare side-effect import only (`import 'toiljs/server/auth'`); not `import type`, not a subpath.
|
|
149
|
+
const re = /(^|[^.\w])import\s+['"]toiljs\/server\/auth['"]/m;
|
|
150
|
+
for (const rel of files) {
|
|
151
|
+
try {
|
|
152
|
+
if (re.test(fs.readFileSync(path.join(root, rel), 'utf8'))) return true;
|
|
153
|
+
} catch {
|
|
154
|
+
// unreadable: skip
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
109
160
|
/**
|
|
110
161
|
* Builds the toilscript server target (which also regenerates `shared/server.ts` via
|
|
111
162
|
* `--rpcModule`) when the project has one, signalled by a `toilconfig.json` at the root. This
|
|
@@ -115,7 +166,7 @@ function serverEntryFiles(root: string): string[] {
|
|
|
115
166
|
* toilconfig entries) so dropped-in `@data`/`@rest` files are picked up. Runs the locally
|
|
116
167
|
* installed `toilscript`, resolved + invoked via Node (no `.bin` shim / PATH assumptions).
|
|
117
168
|
*/
|
|
118
|
-
export async function buildServer(root: string): Promise<void> {
|
|
169
|
+
export async function buildServer(root: string, auth: boolean = false): Promise<void> {
|
|
119
170
|
if (!fs.existsSync(path.join(root, 'toilconfig.json'))) return;
|
|
120
171
|
|
|
121
172
|
// Regenerate the editor-only server-globals d.ts each build (the same way
|
|
@@ -137,6 +188,24 @@ export async function buildServer(root: string): Promise<void> {
|
|
|
137
188
|
// (optimization, features, runtime) still come from the toilconfig's `release` target.
|
|
138
189
|
const files = serverEntryFiles(root);
|
|
139
190
|
|
|
191
|
+
// Built-in auth opt-in: APPEND the framework-shipped auth surface to the entry set BEFORE the tier
|
|
192
|
+
// split, so its `@user`/`@data`/`@database`/`@rest('auth')` decorators weave (a source under the
|
|
193
|
+
// `server/globals` LIB set — or transitively imported from node_modules under the `~lib/` prefix —
|
|
194
|
+
// would NOT weave) and the controller self-mounts at `/auth/*` with no hand-written boilerplate.
|
|
195
|
+
// AuthUser is ordered first (the controller imports it); AuthController lands in the REQUEST tier (it
|
|
196
|
+
// declares `@rest`) and AuthUser is shared into every pass (only `@user`/`@data`). They live under
|
|
197
|
+
// node_modules, so serverEntryFiles never scanned them. Opt-in is either the `server.auth` config flag
|
|
198
|
+
// OR the escape-hatch `import 'toiljs/server/auth'` in a server entry (which only MARKS the intent —
|
|
199
|
+
// it cannot weave on its own, being a `~lib/` import — so the build turns it into the same injection).
|
|
200
|
+
if (auth || serverImportsAuth(root, files)) {
|
|
201
|
+
const authFiles = authEntryFiles(root);
|
|
202
|
+
const authSet = new Set(authFiles);
|
|
203
|
+
files.unshift(...authFiles);
|
|
204
|
+
for (let i = files.length - 1; i >= authFiles.length; i--) {
|
|
205
|
+
if (authSet.has(files[i])) files.splice(i, 1); // defensive dedup (normally none)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
140
209
|
// A project that declares a `@daemon` (L4 cold surface) and/or a `@stream` (L2/L3 stream
|
|
141
210
|
// surface) compiles the ONE source tree into SEPARATE artifacts, one per deployment tier, via
|
|
142
211
|
// one toilscript pass each; a project with only the request surface keeps the default
|
|
@@ -473,7 +542,7 @@ function watchServer(cfg: ResolvedToilConfig, watcher: ViteDevServer['watcher'])
|
|
|
473
542
|
// Recompile emails/*.tsx -> the generated module before the server build,
|
|
474
543
|
// so editing an email template hot-reloads like any other server change.
|
|
475
544
|
renderEmails(cfg)
|
|
476
|
-
.then(() => buildServer(root))
|
|
545
|
+
.then(() => buildServer(root, cfg.auth))
|
|
477
546
|
.then(() => process.stdout.write(pc.green(' ✓ ') + pc.dim('server rebuilt') + '\n'))
|
|
478
547
|
.catch((e: unknown) =>
|
|
479
548
|
process.stdout.write(pc.red(` ✗ server rebuild failed: ${String(e)}`) + '\n'),
|
|
@@ -751,7 +820,7 @@ export async function dev(opts: ToilCommandOptions = {}): Promise<ViteDevServer>
|
|
|
751
820
|
// documented fail-safe 500 until the next full `build`. A no-op without an `ssr = true` route.
|
|
752
821
|
generate(cfg);
|
|
753
822
|
if (hasServer) await extractServerSlots(cfg);
|
|
754
|
-
await buildServer(cfg.root);
|
|
823
|
+
await buildServer(cfg.root, cfg.auth);
|
|
755
824
|
if (hasServer) process.stdout.write(pc.green(' ✓ ') + pc.dim('server built') + '\n');
|
|
756
825
|
|
|
757
826
|
if (!hasServer) {
|
|
@@ -874,7 +943,7 @@ export async function build(opts: ToilCommandOptions = {}): Promise<void> {
|
|
|
874
943
|
// server compiles. (The `HASH` is finalized by the post-Vite `extractTemplates` below, which
|
|
875
944
|
// recompiles the server only if it rotated.) A no-op for a project with no `ssr = true` route.
|
|
876
945
|
const priorServerSlots = hasServer ? await extractServerSlots(cfg) : new Map<string, string>();
|
|
877
|
-
await buildServer(cfg.root);
|
|
946
|
+
await buildServer(cfg.root, cfg.auth);
|
|
878
947
|
if (opts.serverOnly) return;
|
|
879
948
|
if (hasServer)
|
|
880
949
|
process.stdout.write(
|
|
@@ -909,7 +978,7 @@ export async function build(opts: ToilCommandOptions = {}): Promise<void> {
|
|
|
909
978
|
process.stdout.write(
|
|
910
979
|
pc.dim(' SSR template changed; recompiling the server with the new hash…') + '\n',
|
|
911
980
|
);
|
|
912
|
-
await buildServer(cfg.root);
|
|
981
|
+
await buildServer(cfg.root, cfg.auth);
|
|
913
982
|
}
|
|
914
983
|
}
|
|
915
984
|
|