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/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,78 @@ 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, appHasUser: boolean): string[] {
|
|
119
|
+
// EXTEND mode: the app declares its own `@user`, so the build injects the reserved `toilUserId` +
|
|
120
|
+
// `username` identity fields into it (via `--authUser`) and does NOT append the shipped empty `@user`,
|
|
121
|
+
// only the controller. BUILTIN mode (no app `@user`): append both (the shipped empty `SessionUser`
|
|
122
|
+
// gets the same injected identity fields).
|
|
123
|
+
const names = appHasUser ? ['AuthController.ts'] : ['AuthUser.ts', 'AuthController.ts'];
|
|
124
|
+
const primary = names.map((n) => path.posix.join('node_modules/toiljs/server/auth', n));
|
|
125
|
+
if (primary.every((rel) => fs.existsSync(path.join(root, rel)))) return primary;
|
|
126
|
+
// Fallback: locate this running toiljs package dir and make the paths root-relative.
|
|
127
|
+
try {
|
|
128
|
+
const pkgDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
129
|
+
const abs = names.map((n) => path.join(pkgDir, 'server', 'auth', n));
|
|
130
|
+
if (abs.every((p) => fs.existsSync(p)))
|
|
131
|
+
return abs.map((p) => path.relative(root, p).replace(/\\/g, '/'));
|
|
132
|
+
} catch {
|
|
133
|
+
// fall through to the error below
|
|
134
|
+
}
|
|
135
|
+
throw new Error(
|
|
136
|
+
'toiljs: server.auth is enabled but the built-in auth controller ' +
|
|
137
|
+
'(server/auth/AuthController.ts) was not found under node_modules/toiljs. ' +
|
|
138
|
+
'Reinstall toiljs (npm i toiljs), or remove server.auth from toil.config.ts.',
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Whether any server entry declares the escape-hatch `import 'toiljs/server/auth'` (a bare side-effect
|
|
144
|
+
* import) — the lighter opt-in that turns on the built-in auth surface without the `server.auth` config
|
|
145
|
+
* flag. Framework-shipped decorator sources under node_modules only WEAVE as explicit toilscript entries
|
|
146
|
+
* (a transitive import lands them under the `~lib/` LIBRARY prefix, where `@data`/`@rest`/`@user` do not
|
|
147
|
+
* weave), so the bare import is only a SIGNAL: it triggers the same entry injection as the flag. `import
|
|
148
|
+
* type` is skipped (erased). The marker module `server/auth/index.ts` is intentionally empty so it never
|
|
149
|
+
* pulls a conflicting `~lib/` copy of the controller into the compile.
|
|
150
|
+
*/
|
|
151
|
+
/**
|
|
152
|
+
* Whether any server entry declares its own `@user` (EXTEND mode). When built-in auth is on and the app
|
|
153
|
+
* declares a `@user`, the build extends THAT class: toilscript injects the reserved `toilUserId` +
|
|
154
|
+
* `username` fields into it (via `--authUser`, erroring on a name collision) instead of appending the
|
|
155
|
+
* shipped empty built-in `@user`, so the app carries the identity plus its own fields with no boilerplate.
|
|
156
|
+
*/
|
|
157
|
+
function serverDeclaresUser(root: string, files: string[]): boolean {
|
|
158
|
+
const re = /(^|[^.\w])@user\b/m;
|
|
159
|
+
for (const rel of files) {
|
|
160
|
+
try {
|
|
161
|
+
if (re.test(fs.readFileSync(path.join(root, rel), 'utf8'))) return true;
|
|
162
|
+
} catch {
|
|
163
|
+
// unreadable: skip
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function serverImportsAuth(root: string, files: string[]): boolean {
|
|
170
|
+
// Bare side-effect import only (`import 'toiljs/server/auth'`); not `import type`, not a subpath.
|
|
171
|
+
const re = /(^|[^.\w])import\s+['"]toiljs\/server\/auth['"]/m;
|
|
172
|
+
for (const rel of files) {
|
|
173
|
+
try {
|
|
174
|
+
if (re.test(fs.readFileSync(path.join(root, rel), 'utf8'))) return true;
|
|
175
|
+
} catch {
|
|
176
|
+
// unreadable: skip
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
|
|
109
182
|
/**
|
|
110
183
|
* Builds the toilscript server target (which also regenerates `shared/server.ts` via
|
|
111
184
|
* `--rpcModule`) when the project has one, signalled by a `toilconfig.json` at the root. This
|
|
@@ -115,7 +188,7 @@ function serverEntryFiles(root: string): string[] {
|
|
|
115
188
|
* toilconfig entries) so dropped-in `@data`/`@rest` files are picked up. Runs the locally
|
|
116
189
|
* installed `toilscript`, resolved + invoked via Node (no `.bin` shim / PATH assumptions).
|
|
117
190
|
*/
|
|
118
|
-
export async function buildServer(root: string): Promise<void> {
|
|
191
|
+
export async function buildServer(root: string, auth: boolean = false): Promise<void> {
|
|
119
192
|
if (!fs.existsSync(path.join(root, 'toilconfig.json'))) return;
|
|
120
193
|
|
|
121
194
|
// Regenerate the editor-only server-globals d.ts each build (the same way
|
|
@@ -137,6 +210,29 @@ export async function buildServer(root: string): Promise<void> {
|
|
|
137
210
|
// (optimization, features, runtime) still come from the toilconfig's `release` target.
|
|
138
211
|
const files = serverEntryFiles(root);
|
|
139
212
|
|
|
213
|
+
// Built-in auth opt-in: APPEND the framework-shipped auth surface to the entry set BEFORE the tier
|
|
214
|
+
// split, so its `@user`/`@data`/`@database`/`@rest('auth')` decorators weave (a source under the
|
|
215
|
+
// `server/globals` LIB set — or transitively imported from node_modules under the `~lib/` prefix —
|
|
216
|
+
// would NOT weave) and the controller self-mounts at `/auth/*` with no hand-written boilerplate.
|
|
217
|
+
// AuthUser is ordered first (the controller imports it); AuthController lands in the REQUEST tier (it
|
|
218
|
+
// declares `@rest`) and AuthUser is shared into every pass (only `@user`/`@data`). They live under
|
|
219
|
+
// node_modules, so serverEntryFiles never scanned them. Opt-in is either the `server.auth` config flag
|
|
220
|
+
// OR the escape-hatch `import 'toiljs/server/auth'` in a server entry (which only MARKS the intent —
|
|
221
|
+
// it cannot weave on its own, being a `~lib/` import — so the build turns it into the same injection).
|
|
222
|
+
const authOn = auth || serverImportsAuth(root, files);
|
|
223
|
+
if (authOn) {
|
|
224
|
+
// EXTEND vs BUILTIN: if the app declares its own `@user`, extend that class (do not append the
|
|
225
|
+
// shipped `@user`); otherwise append the built-in empty one. Either way toilscript injects the
|
|
226
|
+
// reserved identity fields (`--authUser`, threaded into every pass below).
|
|
227
|
+
const appHasUser = serverDeclaresUser(root, files);
|
|
228
|
+
const authFiles = authEntryFiles(root, appHasUser);
|
|
229
|
+
const authSet = new Set(authFiles);
|
|
230
|
+
files.unshift(...authFiles);
|
|
231
|
+
for (let i = files.length - 1; i >= authFiles.length; i--) {
|
|
232
|
+
if (authSet.has(files[i])) files.splice(i, 1); // defensive dedup (normally none)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
140
236
|
// A project that declares a `@daemon` (L4 cold surface) and/or a `@stream` (L2/L3 stream
|
|
141
237
|
// surface) compiles the ONE source tree into SEPARATE artifacts, one per deployment tier, via
|
|
142
238
|
// one toilscript pass each; a project with only the request surface keeps the default
|
|
@@ -158,6 +254,7 @@ export async function buildServer(root: string): Promise<void> {
|
|
|
158
254
|
mode: 'cold',
|
|
159
255
|
outFile: artifacts.cold,
|
|
160
256
|
withRpc: false,
|
|
257
|
+
authUser: authOn,
|
|
161
258
|
});
|
|
162
259
|
// STREAM pass: --targetMode hot into its OWN `release-stream.wasm`, no client RPC surface
|
|
163
260
|
// (a resident stream box exposes `stream_dispatch`, not the request client surface). Driven
|
|
@@ -167,6 +264,7 @@ export async function buildServer(root: string): Promise<void> {
|
|
|
167
264
|
mode: 'hot',
|
|
168
265
|
outFile: artifacts.stream,
|
|
169
266
|
withRpc: false,
|
|
267
|
+
authUser: authOn,
|
|
170
268
|
});
|
|
171
269
|
// REQUEST pass: the L1 artifact (= `outFile`), WITH the client RPC surface.
|
|
172
270
|
// A pure daemon/stream project (no request files) skips it so toilscript is not handed an
|
|
@@ -181,6 +279,7 @@ export async function buildServer(root: string): Promise<void> {
|
|
|
181
279
|
// the typed `Server.Stream` (class names, message-type encoders, merged @rest type)
|
|
182
280
|
// WITHOUT compiling stream code into release.wasm.
|
|
183
281
|
rpcSurfaceFiles: split.hasStream ? split.stream : undefined,
|
|
282
|
+
authUser: authOn,
|
|
184
283
|
});
|
|
185
284
|
// The stream pass carries no client RPC surface (withRpc:false), so toilscript never emits the
|
|
186
285
|
// `Server.Stream` client into shared/server.ts. Append it here from the compiled stream
|
|
@@ -191,7 +290,12 @@ export async function buildServer(root: string): Promise<void> {
|
|
|
191
290
|
}
|
|
192
291
|
|
|
193
292
|
// Default request-only single-artifact path (no daemon/stream surface).
|
|
194
|
-
await runToilscriptPass(root, binJs, files, {
|
|
293
|
+
await runToilscriptPass(root, binJs, files, {
|
|
294
|
+
mode: null,
|
|
295
|
+
outFile: null,
|
|
296
|
+
withRpc: true,
|
|
297
|
+
authUser: authOn,
|
|
298
|
+
});
|
|
195
299
|
}
|
|
196
300
|
|
|
197
301
|
/** Resolve the locally installed `toilscript` bin via Node (no `.bin` shim / PATH assumptions). */
|
|
@@ -405,6 +509,9 @@ interface PassOptions {
|
|
|
405
509
|
* into this artifact. Lets the request pass emit `Server.Stream` without pulling stream code into
|
|
406
510
|
* release.wasm. */
|
|
407
511
|
readonly rpcSurfaceFiles?: readonly string[];
|
|
512
|
+
/** Built-in auth: pass `--authUser` so toilscript injects the reserved `toilUserId` + `username` fields
|
|
513
|
+
* into the single `@user`. Threaded into every pass so the `@user` codec is identical across tiers. */
|
|
514
|
+
readonly authUser?: boolean;
|
|
408
515
|
}
|
|
409
516
|
|
|
410
517
|
/** Run one toilscript pass. The toilscript CLI flag is `--targetMode` (camelCase). */
|
|
@@ -423,6 +530,7 @@ function runToilscriptPass(
|
|
|
423
530
|
if (opts.withRpc) args.push('--rpcModule', 'shared/server.ts');
|
|
424
531
|
if (opts.rpcSurfaceFiles)
|
|
425
532
|
for (const surfaceFile of opts.rpcSurfaceFiles) args.push('--rpcSurfaceFiles', surfaceFile);
|
|
533
|
+
if (opts.authUser) args.push('--authUser');
|
|
426
534
|
// Each pass is handed its OWN entry subset (the per-tier `files`); suppress the toilconfig
|
|
427
535
|
// `entries` so toilscript does not ALSO append every project entry to every pass (which would
|
|
428
536
|
// pull, e.g., a `@stream` class into the cold daemon pass). serverEntryFiles already folds
|
|
@@ -473,7 +581,7 @@ function watchServer(cfg: ResolvedToilConfig, watcher: ViteDevServer['watcher'])
|
|
|
473
581
|
// Recompile emails/*.tsx -> the generated module before the server build,
|
|
474
582
|
// so editing an email template hot-reloads like any other server change.
|
|
475
583
|
renderEmails(cfg)
|
|
476
|
-
.then(() => buildServer(root))
|
|
584
|
+
.then(() => buildServer(root, cfg.auth))
|
|
477
585
|
.then(() => process.stdout.write(pc.green(' ✓ ') + pc.dim('server rebuilt') + '\n'))
|
|
478
586
|
.catch((e: unknown) =>
|
|
479
587
|
process.stdout.write(pc.red(` ✗ server rebuild failed: ${String(e)}`) + '\n'),
|
|
@@ -751,7 +859,7 @@ export async function dev(opts: ToilCommandOptions = {}): Promise<ViteDevServer>
|
|
|
751
859
|
// documented fail-safe 500 until the next full `build`. A no-op without an `ssr = true` route.
|
|
752
860
|
generate(cfg);
|
|
753
861
|
if (hasServer) await extractServerSlots(cfg);
|
|
754
|
-
await buildServer(cfg.root);
|
|
862
|
+
await buildServer(cfg.root, cfg.auth);
|
|
755
863
|
if (hasServer) process.stdout.write(pc.green(' ✓ ') + pc.dim('server built') + '\n');
|
|
756
864
|
|
|
757
865
|
if (!hasServer) {
|
|
@@ -874,7 +982,7 @@ export async function build(opts: ToilCommandOptions = {}): Promise<void> {
|
|
|
874
982
|
// server compiles. (The `HASH` is finalized by the post-Vite `extractTemplates` below, which
|
|
875
983
|
// recompiles the server only if it rotated.) A no-op for a project with no `ssr = true` route.
|
|
876
984
|
const priorServerSlots = hasServer ? await extractServerSlots(cfg) : new Map<string, string>();
|
|
877
|
-
await buildServer(cfg.root);
|
|
985
|
+
await buildServer(cfg.root, cfg.auth);
|
|
878
986
|
if (opts.serverOnly) return;
|
|
879
987
|
if (hasServer)
|
|
880
988
|
process.stdout.write(
|
|
@@ -909,7 +1017,7 @@ export async function build(opts: ToilCommandOptions = {}): Promise<void> {
|
|
|
909
1017
|
process.stdout.write(
|
|
910
1018
|
pc.dim(' SSR template changed; recompiling the server with the new hash…') + '\n',
|
|
911
1019
|
);
|
|
912
|
-
await buildServer(cfg.root);
|
|
1020
|
+
await buildServer(cfg.root, cfg.auth);
|
|
913
1021
|
}
|
|
914
1022
|
}
|
|
915
1023
|
|