toiljs 0.0.116 → 0.0.118
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 +14 -0
- package/README.md +16 -7
- package/build/backend/.tsbuildinfo +1 -1
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/index.js +2375 -258
- package/build/client/.tsbuildinfo +1 -1
- package/build/client/auth.d.ts +3 -0
- package/build/client/auth.js +19 -10
- package/build/client/dev/devtools.js +1 -1
- package/build/client/dev/error-overlay.js +1 -1
- package/build/client/index.d.ts +1 -1
- package/build/client/index.js +1 -1
- package/build/client/navigation/navigation.js +28 -3
- package/build/client/routing/Router.js +5 -2
- package/build/client/routing/mount.js +1 -1
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/auth-emails.js +7 -3
- package/build/compiler/emails.js +1 -1
- package/build/compiler/index.d.ts +1 -0
- package/build/compiler/index.js +26 -5
- package/build/compiler/pages.js +2 -3
- package/build/compiler/prerender.d.ts +3 -8
- package/build/compiler/prerender.js +88 -127
- package/build/compiler/ssg.js +3 -4
- package/build/compiler/template-build.js +3 -4
- package/build/compiler/toil-docs.generated.js +8 -7
- package/build/compiler/vite.js +4 -6
- package/build/devserver/.tsbuildinfo +1 -1
- package/build/devserver/db/database.js +7 -3
- package/build/devserver/db/types.js +1 -1
- package/build/devserver/production.js +4 -3
- package/build/devserver/runtime/crypto.d.ts +1 -1
- package/build/io/.tsbuildinfo +1 -1
- package/build/logger/.tsbuildinfo +1 -1
- package/build/shared/.tsbuildinfo +1 -1
- package/examples/basic/client/routes/analytics.tsx +7 -5
- package/examples/basic/client/routes/pq.tsx +2 -1
- package/examples/basic/server/tsconfig.json +1 -3
- package/package.json +45 -44
- package/presets/no-uint8array-tostring.js +120 -153
- package/presets/oxlint.d.ts +5 -0
- package/presets/oxlint.js +224 -0
- package/server/auth/AuthController.ts +53 -14
- package/server/auth/FactorSettings.ts +41 -0
- package/server/auth/SessionEpochs.ts +58 -0
- package/server/auth/SessionRevocations.ts +13 -0
- package/server/globals/auth.ts +27 -2
- package/server/runtime/tsconfig.json +1 -3
- package/tsconfig.json +2 -11
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -90
- package/.github/ISSUE_TEMPLATE/config.yml +0 -8
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -43
- package/.github/changelog-config.json +0 -45
- package/.github/dependabot.yml +0 -27
- package/.github/workflows/ci.yml +0 -222
- package/.vscode/settings.json +0 -10
- package/docs/README.md +0 -107
- package/docs/auth/README.md +0 -104
- package/docs/auth/configuration.md +0 -105
- package/docs/auth/emails.md +0 -92
- package/docs/auth/extending.md +0 -202
- package/docs/auth/how-it-works.md +0 -138
- package/docs/auth/usage.md +0 -188
- package/docs/backend/README.md +0 -143
- package/docs/backend/data.md +0 -351
- package/docs/backend/rest.md +0 -402
- package/docs/backend/rpc.md +0 -226
- package/docs/background/README.md +0 -114
- package/docs/background/daemons.md +0 -276
- package/docs/background/derive.md +0 -179
- package/docs/cli/README.md +0 -379
- package/docs/concepts/ai-guide.md +0 -132
- package/docs/concepts/config.md +0 -416
- package/docs/concepts/decorators.md +0 -127
- package/docs/concepts/security.md +0 -108
- package/docs/concepts/tiers.md +0 -166
- package/docs/concepts/types.md +0 -216
- package/docs/database/README.md +0 -143
- package/docs/database/capacity.md +0 -350
- package/docs/database/counters.md +0 -174
- package/docs/database/documents.md +0 -277
- package/docs/database/events.md +0 -307
- package/docs/database/membership.md +0 -246
- package/docs/database/setup.md +0 -155
- package/docs/database/unique.md +0 -216
- package/docs/database/views.md +0 -246
- package/docs/frontend/README.md +0 -104
- package/docs/frontend/components.md +0 -286
- package/docs/frontend/data-fetching.md +0 -260
- package/docs/frontend/images.md +0 -148
- package/docs/frontend/metadata.md +0 -344
- package/docs/frontend/navigation.md +0 -296
- package/docs/frontend/rendering.md +0 -236
- package/docs/frontend/routing.md +0 -346
- package/docs/frontend/scripts.md +0 -118
- package/docs/frontend/search.md +0 -191
- package/docs/frontend/styling.md +0 -147
- package/docs/frontend/toil-global.md +0 -216
- package/docs/getting-started/README.md +0 -81
- package/docs/getting-started/create-project.md +0 -131
- package/docs/getting-started/deploy.md +0 -101
- package/docs/getting-started/first-app.md +0 -215
- package/docs/getting-started/installation.md +0 -124
- package/docs/getting-started/migrating.md +0 -125
- package/docs/getting-started/project-structure.md +0 -163
- package/docs/introduction/README.md +0 -66
- package/docs/introduction/design-principles.md +0 -81
- package/docs/introduction/distributed.md +0 -91
- package/docs/introduction/how-it-works.md +0 -121
- package/docs/introduction/hyperscale.md +0 -70
- package/docs/introduction/modern-stack.md +0 -85
- package/docs/introduction/vs-other-frameworks.md +0 -64
- package/docs/introduction/why-toil.md +0 -99
- package/docs/llms.txt +0 -95
- package/docs/realtime/README.md +0 -102
- package/docs/realtime/channels.md +0 -211
- package/docs/realtime/streams.md +0 -369
- package/docs/services/README.md +0 -60
- package/docs/services/analytics.md +0 -279
- package/docs/services/caching.md +0 -175
- package/docs/services/cookies.md +0 -235
- package/docs/services/crypto.md +0 -209
- package/docs/services/email.md +0 -289
- package/docs/services/environment.md +0 -141
- package/docs/services/ratelimit.md +0 -174
- package/docs/services/time.md +0 -85
- package/eslint.config.js +0 -57
- package/examples/basic/client/routes/docs/[...slug].tsx +0 -12
- package/presets/eslint.js +0 -88
- package/src/backend/index.ts +0 -204
- package/src/cli/configure.ts +0 -363
- package/src/cli/create.ts +0 -924
- package/src/cli/db.ts +0 -158
- package/src/cli/diagnostics.ts +0 -665
- package/src/cli/doctor.ts +0 -982
- package/src/cli/features.ts +0 -170
- package/src/cli/index.ts +0 -326
- package/src/cli/notify.ts +0 -120
- package/src/cli/proc.ts +0 -56
- package/src/cli/ui.ts +0 -169
- package/src/cli/update.ts +0 -248
- package/src/cli/updates.ts +0 -92
- package/src/cli/validate.ts +0 -31
- package/src/cli/version-check.ts +0 -125
- package/src/client/auth.ts +0 -869
- package/src/client/channel/channel.ts +0 -146
- package/src/client/components/Form.tsx +0 -65
- package/src/client/components/Image.tsx +0 -160
- package/src/client/components/Script.tsx +0 -113
- package/src/client/components/Slot.tsx +0 -21
- package/src/client/dev/devtools.tsx +0 -1089
- package/src/client/dev/error-overlay.tsx +0 -225
- package/src/client/errors.ts +0 -11
- package/src/client/head/head.ts +0 -182
- package/src/client/head/metadata.ts +0 -110
- package/src/client/index.ts +0 -130
- package/src/client/navigation/Link.tsx +0 -108
- package/src/client/navigation/NavLink.tsx +0 -86
- package/src/client/navigation/navigation.ts +0 -235
- package/src/client/navigation/prefetch.ts +0 -169
- package/src/client/navigation/scroll.ts +0 -53
- package/src/client/routing/Router.tsx +0 -216
- package/src/client/routing/action.ts +0 -122
- package/src/client/routing/error-boundary.tsx +0 -43
- package/src/client/routing/hooks.ts +0 -140
- package/src/client/routing/lazy.ts +0 -77
- package/src/client/routing/loader.ts +0 -381
- package/src/client/routing/match.ts +0 -47
- package/src/client/routing/mount.tsx +0 -90
- package/src/client/routing/params-context.ts +0 -10
- package/src/client/routing/slot-context.ts +0 -7
- package/src/client/rpc.ts +0 -97
- package/src/client/search/search.ts +0 -189
- package/src/client/search/use-page-search.ts +0 -73
- package/src/client/ssr/markers.tsx +0 -172
- package/src/client/stream/client.ts +0 -237
- package/src/client/types.ts +0 -73
- package/src/compiler/auth-emails.ts +0 -312
- package/src/compiler/config.ts +0 -369
- package/src/compiler/docs.ts +0 -127
- package/src/compiler/email-preview.ts +0 -314
- package/src/compiler/emails.ts +0 -404
- package/src/compiler/fonts.ts +0 -89
- package/src/compiler/generate.ts +0 -471
- package/src/compiler/image-blur.ts +0 -42
- package/src/compiler/image-report.ts +0 -88
- package/src/compiler/index.ts +0 -1362
- package/src/compiler/pages.ts +0 -49
- package/src/compiler/plugin.ts +0 -391
- package/src/compiler/prerender.ts +0 -263
- package/src/compiler/routes.ts +0 -170
- package/src/compiler/seo.ts +0 -415
- package/src/compiler/sri.ts +0 -144
- package/src/compiler/ssg.ts +0 -177
- package/src/compiler/ssr-codegen.ts +0 -85
- package/src/compiler/template-build.ts +0 -676
- package/src/compiler/template.ts +0 -262
- package/src/compiler/toil-docs.generated.ts +0 -77
- package/src/compiler/vite.ts +0 -245
- package/src/devserver/analytics/index.ts +0 -317
- package/src/devserver/config/dotenv.ts +0 -102
- package/src/devserver/config/env.ts +0 -30
- package/src/devserver/config/ratelimit.ts +0 -123
- package/src/devserver/daemon/catalog.ts +0 -120
- package/src/devserver/daemon/cron.ts +0 -87
- package/src/devserver/daemon/host.ts +0 -134
- package/src/devserver/daemon/index.ts +0 -369
- package/src/devserver/daemon/runtime.ts +0 -48
- package/src/devserver/db/catalog.ts +0 -109
- package/src/devserver/db/database.ts +0 -1834
- package/src/devserver/db/derives.ts +0 -121
- package/src/devserver/db/index.ts +0 -20
- package/src/devserver/db/routeKinds.ts +0 -191
- package/src/devserver/db/types.ts +0 -168
- package/src/devserver/email/caps.ts +0 -0
- package/src/devserver/email/config.ts +0 -141
- package/src/devserver/email/console.ts +0 -348
- package/src/devserver/email/index.ts +0 -155
- package/src/devserver/email/providers.ts +0 -131
- package/src/devserver/email/status.ts +0 -23
- package/src/devserver/email/validate.ts +0 -37
- package/src/devserver/email/wire.ts +0 -55
- package/src/devserver/http/cache.ts +0 -0
- package/src/devserver/http/envelope.ts +0 -157
- package/src/devserver/http/proxy.ts +0 -172
- package/src/devserver/http/runtime.ts +0 -387
- package/src/devserver/index.ts +0 -43
- package/src/devserver/production-ipc.ts +0 -63
- package/src/devserver/production-worker.ts +0 -83
- package/src/devserver/production.ts +0 -718
- package/src/devserver/runtime/crypto.ts +0 -587
- package/src/devserver/runtime/host.ts +0 -427
- package/src/devserver/runtime/module.ts +0 -442
- package/src/devserver/server.ts +0 -251
- package/src/devserver/ssr.ts +0 -208
- package/src/devserver/stream/catalog.ts +0 -106
- package/src/devserver/stream/host.ts +0 -42
- package/src/devserver/stream/index.ts +0 -301
- package/src/devserver/stream/manager.ts +0 -167
- package/src/devserver/stream/router.ts +0 -101
- package/src/devserver/stream/wire.ts +0 -58
- package/src/devserver/stream/ws.ts +0 -78
- package/src/devserver/wasm/sections.ts +0 -59
- package/src/devserver/wasm/surface.ts +0 -115
- package/src/io/FastMap.ts +0 -151
- package/src/io/FastSet.ts +0 -113
- package/src/io/codec.ts +0 -362
- package/src/io/index.ts +0 -10
- package/src/io/lengths.ts +0 -14
- package/src/io/types.ts +0 -19
- package/src/logger/index.ts +0 -22
- package/src/shared/index.ts +0 -46
- package/test/analytics-dev.test.ts +0 -113
- package/test/assembly/aspect-shim.ts +0 -33
- package/test/assembly/cookie.spec.ts +0 -303
- package/test/assembly/example.spec.ts +0 -22
- package/test/assembly/ssr.spec.ts +0 -95
- package/test/assembly.test.ts +0 -59
- package/test/auth-emails.test.ts +0 -118
- package/test/built-ssr.test.ts +0 -148
- package/test/channel.test.ts +0 -21
- package/test/config-host-port.test.ts +0 -59
- package/test/configure.test.ts +0 -94
- package/test/daemon-build.test.ts +0 -278
- package/test/daemon-catalog.test.ts +0 -349
- package/test/daemon-emulation.test.ts +0 -256
- package/test/db.test.ts +0 -0
- package/test/devserver-database.test.ts +0 -1102
- package/test/devserver-email.test.ts +0 -306
- package/test/devserver-pqauth.test.ts +0 -93
- package/test/devserver-secrets.test.ts +0 -63
- package/test/devserver.test.ts +0 -259
- package/test/doctor.test.ts +0 -298
- package/test/dom/Image.test.tsx +0 -144
- package/test/dom/Link.test.tsx +0 -67
- package/test/dom/NavLink.test.tsx +0 -37
- package/test/dom/Script.test.tsx +0 -48
- package/test/dom/action.test.tsx +0 -146
- package/test/dom/error-overlay.test.tsx +0 -44
- package/test/dom/loader.test.tsx +0 -121
- package/test/dom/navigation.test.ts +0 -59
- package/test/dom/revalidate.test.tsx +0 -38
- package/test/dom/route-head.test.tsx +0 -56
- package/test/dom/router-loading.test.tsx +0 -44
- package/test/dom/scroll.test.ts +0 -56
- package/test/dom/slot.test.tsx +0 -131
- package/test/dom/use-metadata.test.tsx +0 -58
- package/test/dom/view-transitions.test.tsx +0 -53
- package/test/dynamic-routing.test.ts +0 -132
- package/test/email-console.test.ts +0 -86
- package/test/email-preview.test.ts +0 -73
- package/test/emails.test.ts +0 -58
- package/test/errors.test.ts +0 -21
- package/test/example-guestbook.test.ts +0 -123
- package/test/features.test.ts +0 -149
- package/test/fixtures/bignum-wire/spec.ts +0 -25
- package/test/fixtures/daemon-app.ts +0 -65
- package/test/fixtures/stream-echo.ts +0 -26
- package/test/fixtures/stream-gate.ts +0 -28
- package/test/fixtures/stream-trap.ts +0 -18
- package/test/fixtures/stream-typed.ts +0 -41
- package/test/fonts.test.ts +0 -28
- package/test/global-setup.ts +0 -17
- package/test/head.test.ts +0 -42
- package/test/image-blur.test.ts +0 -50
- package/test/io.test.ts +0 -117
- package/test/metadata.test.ts +0 -40
- package/test/navlink.test.ts +0 -28
- package/test/pages.test.ts +0 -105
- package/test/placeholder.test.ts +0 -9
- package/test/pqauth-e2e.test.ts +0 -216
- package/test/pqauth-email.test.ts +0 -801
- package/test/prerender.test.ts +0 -116
- package/test/prettier-plugin.test.ts +0 -46
- package/test/routes.test.ts +0 -76
- package/test/rpc-bignum-wire.test.ts +0 -164
- package/test/rpc-dispatch.test.ts +0 -132
- package/test/rpc-kinds.test.ts +0 -18
- package/test/rpc.test.ts +0 -66
- package/test/search.test.ts +0 -114
- package/test/seo.test.ts +0 -175
- package/test/slot-layouts.test.ts +0 -69
- package/test/sri.test.ts +0 -129
- package/test/ssg.test.ts +0 -36
- package/test/ssr-hydration.test.tsx +0 -122
- package/test/ssr-render.test.ts +0 -197
- package/test/ssr-template.test.tsx +0 -482
- package/test/stream-emulation.test.ts +0 -479
- package/test/ui.test.ts +0 -40
- package/test/update.test.ts +0 -58
- package/test/validate.test.ts +0 -42
- package/test/version-check.test.ts +0 -130
- package/tests/data-parity/generated-parity.ts +0 -99
- package/tests/data-parity/parity.ts +0 -80
- package/tests/data-parity/spec.ts +0 -46
package/src/client/types.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Public router types shared across the client runtime. Kept dependency-free (type-only React
|
|
3
|
-
* imports) so any module can import them without pulling in component or DOM code.
|
|
4
|
-
*/
|
|
5
|
-
import type { ComponentType, ReactNode } from 'react';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Augmentation point for the project's generated route types. The compiler emits (into
|
|
9
|
-
* `toil-routes.d.ts`) `declare module 'toiljs/client' { interface Register { routePath: <union> } }`,
|
|
10
|
-
* which narrows {@link RoutePath} from `string` to the project's actual routes.
|
|
11
|
-
*/
|
|
12
|
-
export interface Register {}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Union of the project's route paths, static routes as literals, dynamic/catch-all as
|
|
16
|
-
* `` `…/${string}` `` templates. Falls back to `string` before the types are generated.
|
|
17
|
-
*/
|
|
18
|
-
export type RoutePath = Register extends { routePath: infer P }
|
|
19
|
-
? P extends string
|
|
20
|
-
? P
|
|
21
|
-
: string
|
|
22
|
-
: string;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* An href accepted by `Link` / `NavLink` / `navigate`: a known {@link RoutePath} (optionally with
|
|
26
|
-
* `?query` or `#hash`), or an absolute/protocol URL (`https:`, `mailto:`, …). When routes haven't
|
|
27
|
-
* been generated yet, this is just `string`.
|
|
28
|
-
*/
|
|
29
|
-
export type Href =
|
|
30
|
-
| RoutePath
|
|
31
|
-
| `${RoutePath}?${string}`
|
|
32
|
-
| `${RoutePath}#${string}`
|
|
33
|
-
| `${string}:${string}`;
|
|
34
|
-
|
|
35
|
-
/** Lazy loader for a layout component (wraps children). */
|
|
36
|
-
export type LayoutComponentLoader = () => Promise<{
|
|
37
|
-
default: ComponentType<{ children?: ReactNode }>;
|
|
38
|
-
}>;
|
|
39
|
-
|
|
40
|
-
/** Props passed to an `error.tsx` / `global-error.tsx` component. */
|
|
41
|
-
export interface RouteErrorProps {
|
|
42
|
-
readonly error: Error;
|
|
43
|
-
readonly reset: () => void;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Lazy loader for an error component (`error.tsx` / `global-error.tsx`), or `null` if none. */
|
|
47
|
-
export type ErrorComponentLoader =
|
|
48
|
-
| (() => Promise<{ default: ComponentType<RouteErrorProps> }>)
|
|
49
|
-
| null;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* A route entry produced by the compiler: a URL pattern, a lazy loader for its page component, and
|
|
53
|
-
* the chain of nested layout loaders (shallowest → deepest, from nested `layout.tsx` files) that wrap it.
|
|
54
|
-
*/
|
|
55
|
-
export interface RouteDef {
|
|
56
|
-
readonly pattern: string;
|
|
57
|
-
readonly load: () => Promise<{ default: ComponentType }>;
|
|
58
|
-
readonly layouts?: readonly LayoutComponentLoader[];
|
|
59
|
-
/** `template.tsx` chain (root → nested), like layouts, but re-mounted on each navigation. */
|
|
60
|
-
readonly templates?: readonly LayoutComponentLoader[];
|
|
61
|
-
/** Nearest `loading.tsx`, shown as the Suspense fallback while this route loads. */
|
|
62
|
-
readonly loading?: () => Promise<{ default: ComponentType }>;
|
|
63
|
-
/** Nearest `error.tsx`, rendered by an error boundary around this route. */
|
|
64
|
-
readonly errorComponent?: () => Promise<{ default: ComponentType<RouteErrorProps> }>;
|
|
65
|
-
/** Intercepting route (`(.)`/`(..)`/`(...)`), matched in its slot only on soft navigation. */
|
|
66
|
-
readonly intercept?: boolean;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** Optional root layout loader (wraps every page). `null` when the project defines no layout. */
|
|
70
|
-
export type LayoutLoader = LayoutComponentLoader | null;
|
|
71
|
-
|
|
72
|
-
/** Optional custom not-found (404) page loader, rendered when no route matches. */
|
|
73
|
-
export type NotFoundLoader = (() => Promise<{ default: ComponentType }>) | null;
|
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Built-in auth email templates + their per-app OVERRIDE pipeline.
|
|
3
|
-
*
|
|
4
|
-
* The built-in auth controller (`server/auth/AuthController.ts`) sends three
|
|
5
|
-
* transactional emails: the email-verification link, the password-reset link,
|
|
6
|
-
* and the 2FA code. Their default subject/text/html live here as the single
|
|
7
|
-
* source of truth. An app overrides any of them by dropping a reserved-name
|
|
8
|
-
* React template in `emails/`:
|
|
9
|
-
*
|
|
10
|
-
* emails/auth-confirm.tsx -> email verification (interpolates `{link}`)
|
|
11
|
-
* emails/auth-reset.tsx -> password reset (interpolates `{link}`)
|
|
12
|
-
* emails/auth-2fa.tsx -> 2FA code (interpolates `{code}`)
|
|
13
|
-
*
|
|
14
|
-
* Whenever auth is on, the build (re)writes an ambient `_auth_emails.ts` into the
|
|
15
|
-
* toiljs `server/globals` LIB dir, exposing `AuthEmail.confirm/reset/twofa` baked
|
|
16
|
-
* with the app's override when present and the default otherwise. Because it rides
|
|
17
|
-
* the same `lib` set as `EmailService`/`AuthService`, the node_modules-compiled
|
|
18
|
-
* `AuthController` calls it with NO import (an exported namespace in a plain app
|
|
19
|
-
* entry would be module-scoped, not global). Every send goes through
|
|
20
|
-
* `EmailTemplate.sendDetached`, so overriding a template can never reintroduce the
|
|
21
|
-
* auth email-enumeration timing oracle the detached send closes.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import fs from 'node:fs';
|
|
25
|
-
import path from 'node:path';
|
|
26
|
-
|
|
27
|
-
import pc from 'picocolors';
|
|
28
|
-
import { createServer } from 'vite';
|
|
29
|
-
|
|
30
|
-
import type { ResolvedToilConfig } from './config.js';
|
|
31
|
-
import { RESERVED_AUTH_EMAIL_NAMES, renderEmailFile, toPascal, type RenderedEmail } from './emails.js';
|
|
32
|
-
import { createViteConfig } from './vite.js';
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* The APP-LOCAL library directory the auth-email module is generated into,
|
|
36
|
-
* relative to the project root (posix, for the toilscript `--lib` CLI arg). It is
|
|
37
|
-
* passed to the compiler via `--lib` (see runToilscriptPass), which "uses exports
|
|
38
|
-
* of all top-level files at this path as globals". That is what makes the
|
|
39
|
-
* generated `AuthEmail` namespace resolvable from the node_modules-compiled
|
|
40
|
-
* `AuthController` with NO import, the same way `EmailService`/`AuthService` are
|
|
41
|
-
* ambient. Generating HERE (the app's own gitignored `.toil/`) rather than into
|
|
42
|
-
* `node_modules/toiljs/server/globals` keeps it PER-APP: a pnpm/workspace/linked
|
|
43
|
-
* install physically shares that package dir, so writing there would let one app
|
|
44
|
-
* clobber another's templates (or delete the file another app is compiling), and
|
|
45
|
-
* a read-only store would fail the build. `.toil` is always app-writable.
|
|
46
|
-
*/
|
|
47
|
-
export const AUTH_EMAILS_LIB_SUBDIR = '.toil/authlib';
|
|
48
|
-
|
|
49
|
-
/** The reserved override names (PascalCase), one per built-in auth email. */
|
|
50
|
-
type AuthName = 'AuthConfirm' | 'AuthReset' | 'Auth2fa';
|
|
51
|
-
|
|
52
|
-
/** One built-in auth email: its reserved override name, the token it fills, the
|
|
53
|
-
* host-side `purpose` tag, and the default subject/text/html (with `{{token}}`). */
|
|
54
|
-
interface AuthEmailSpec {
|
|
55
|
-
/** The generated `AuthEmail.<fn>` this feeds. */
|
|
56
|
-
readonly fn: 'confirm' | 'reset' | 'twofa';
|
|
57
|
-
/** The token that MUST be present in an override (the link or code). */
|
|
58
|
-
readonly requiredToken: 'link' | 'code';
|
|
59
|
-
/** Every token the framework fills for this email (an override may use any subset). */
|
|
60
|
-
readonly providedTokens: readonly string[];
|
|
61
|
-
/** Host dedup/abuse tag; must match what AuthController passed before. */
|
|
62
|
-
readonly purpose: string;
|
|
63
|
-
/** Default subject. Empty (and ignored) for `twofa`: AuthController passes login/setup subjects. */
|
|
64
|
-
readonly defaultSubject: string;
|
|
65
|
-
readonly defaultText: string;
|
|
66
|
-
readonly defaultHtml: string;
|
|
67
|
-
/** Whether the subject is a runtime arg (twofa) rather than a baked default (confirm/reset). */
|
|
68
|
-
readonly subjectFromArg: boolean;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** The reserved override name -> its spec. Keys mirror {@link RESERVED_AUTH_EMAIL_NAMES}. */
|
|
72
|
-
const SPECS: Record<AuthName, AuthEmailSpec> = {
|
|
73
|
-
AuthConfirm: {
|
|
74
|
-
fn: 'confirm',
|
|
75
|
-
requiredToken: 'link',
|
|
76
|
-
providedTokens: ['link'],
|
|
77
|
-
purpose: 'verify',
|
|
78
|
-
subjectFromArg: false,
|
|
79
|
-
defaultSubject: 'Confirm your account',
|
|
80
|
-
defaultText: 'Confirm your account by opening this link:\n{{link}}\n',
|
|
81
|
-
defaultHtml:
|
|
82
|
-
'<p>Confirm your account by clicking the link below:</p>' +
|
|
83
|
-
'<p><a href="{{link}}">Confirm my account</a></p>' +
|
|
84
|
-
'<p>Or paste this into your browser:<br>{{link}}</p>',
|
|
85
|
-
},
|
|
86
|
-
AuthReset: {
|
|
87
|
-
fn: 'reset',
|
|
88
|
-
requiredToken: 'link',
|
|
89
|
-
providedTokens: ['link'],
|
|
90
|
-
purpose: 'reset',
|
|
91
|
-
subjectFromArg: false,
|
|
92
|
-
defaultSubject: 'Reset your password',
|
|
93
|
-
defaultText: 'Reset your password by opening this link:\n{{link}}\n',
|
|
94
|
-
defaultHtml:
|
|
95
|
-
'<p>We received a request to reset your password. Click the link below:</p>' +
|
|
96
|
-
'<p><a href="{{link}}">Reset my password</a></p>' +
|
|
97
|
-
'<p>If you did not request this, you can ignore this email.</p>' +
|
|
98
|
-
'<p>Or paste this into your browser:<br>{{link}}</p>',
|
|
99
|
-
},
|
|
100
|
-
Auth2fa: {
|
|
101
|
-
fn: 'twofa',
|
|
102
|
-
requiredToken: 'code',
|
|
103
|
-
// `code` is the 6-digit code; `action` is the flow phrase AuthController passes
|
|
104
|
-
// ("sign in" / "enable two-factor authentication" / "turn off two-factor
|
|
105
|
-
// authentication"), so one 2FA template reads correctly for login vs setup.
|
|
106
|
-
providedTokens: ['code', 'action'],
|
|
107
|
-
purpose: '2fa',
|
|
108
|
-
subjectFromArg: true,
|
|
109
|
-
// Subject is supplied by AuthController (login vs setup), so it is not baked.
|
|
110
|
-
defaultSubject: '',
|
|
111
|
-
defaultText:
|
|
112
|
-
'Your verification code is {{code}}.\n' +
|
|
113
|
-
'Enter it to {{action}}. It expires in a few minutes. If you did not request it, ignore this email.\n',
|
|
114
|
-
defaultHtml:
|
|
115
|
-
'<p>Your verification code is:</p>' +
|
|
116
|
-
'<p style="font-size:28px;font-weight:bold;letter-spacing:4px">{{code}}</p>' +
|
|
117
|
-
'<p>Enter it to {{action}}. It expires in a few minutes. If you did not request it, ignore this email.</p>',
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const AUTH_NAMES = Object.keys(SPECS) as AuthName[];
|
|
122
|
-
|
|
123
|
-
/** The effective (override-or-default) parts for each auth email. */
|
|
124
|
-
type Parts = Record<AuthName, Effective>;
|
|
125
|
-
interface Effective {
|
|
126
|
-
readonly subject: string;
|
|
127
|
-
readonly text: string;
|
|
128
|
-
readonly html: string;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const GENERATED_BASENAME = '_auth_emails.ts';
|
|
132
|
-
|
|
133
|
-
function warn(msg: string): void {
|
|
134
|
-
process.stderr.write(` toil: auth emails ${msg}\n`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** A valid AS/JS string literal (double-quoted, fully escaped). */
|
|
138
|
-
function asLit(s: string): string {
|
|
139
|
-
return JSON.stringify(s);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** Every reserved auth-override file present in `emails/`, by PascalCase name. */
|
|
143
|
-
function reservedFilesIn(emailsDir: string): Map<AuthName, string> {
|
|
144
|
-
const out = new Map<AuthName, string>();
|
|
145
|
-
if (!fs.existsSync(emailsDir)) return out;
|
|
146
|
-
for (const f of fs.readdirSync(emailsDir).sort()) {
|
|
147
|
-
if (!/\.(tsx|jsx)$/.test(f)) continue;
|
|
148
|
-
const name = toPascal(f.replace(/\.(tsx|jsx)$/, ''));
|
|
149
|
-
if (RESERVED_AUTH_EMAIL_NAMES.has(name) && !out.has(name as AuthName))
|
|
150
|
-
out.set(name as AuthName, f);
|
|
151
|
-
}
|
|
152
|
-
return out;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Fold a rendered override into the effective parts for `name`, warning about
|
|
157
|
-
* templates that will not interpolate correctly (a missing required token means
|
|
158
|
-
* the link/code would be absent; an extra token renders empty). A `subject` the
|
|
159
|
-
* renderer defaulted to the module name is treated as "no custom subject".
|
|
160
|
-
*/
|
|
161
|
-
function effectiveFromOverride(name: AuthName, spec: AuthEmailSpec, r: RenderedEmail): Effective {
|
|
162
|
-
if (!r.tokens.includes(spec.requiredToken))
|
|
163
|
-
warn(
|
|
164
|
-
`override emails/${name} never uses the {${spec.requiredToken}} prop, so the ` +
|
|
165
|
-
`${spec.requiredToken === 'link' ? 'link' : 'code'} will be missing from the email.`,
|
|
166
|
-
);
|
|
167
|
-
for (const t of r.tokens)
|
|
168
|
-
if (!spec.providedTokens.includes(t))
|
|
169
|
-
warn(
|
|
170
|
-
`override emails/${name} uses {${t}}, which auth does not provide (only ` +
|
|
171
|
-
`${spec.providedTokens.map((x) => '{' + x + '}').join(', ')}); it will render empty.`,
|
|
172
|
-
);
|
|
173
|
-
// For twofa the subject is a runtime arg; for confirm/reset use the template's
|
|
174
|
-
// subject unless it defaulted to the component name (no `export const subject`).
|
|
175
|
-
const subject = spec.subjectFromArg || r.subject === name ? spec.defaultSubject : r.subject;
|
|
176
|
-
return { subject, text: r.text, html: r.html };
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/** Codegen one `AuthEmail.<fn>` function. `twofa` takes its subject as an arg. */
|
|
180
|
-
function emitFn(spec: AuthEmailSpec, eff: Effective): string[] {
|
|
181
|
-
let sig: string;
|
|
182
|
-
let subjectExpr: string;
|
|
183
|
-
let sets: string[];
|
|
184
|
-
if (spec.fn === 'twofa') {
|
|
185
|
-
// twofa takes its subject AND its action phrase as runtime args (login vs setup).
|
|
186
|
-
sig = 'twofa(to: string, code: string, subject: string, action: string): void';
|
|
187
|
-
subjectExpr = 'subject';
|
|
188
|
-
sets = ['v.set("code", code);', 'v.set("action", action);'];
|
|
189
|
-
} else {
|
|
190
|
-
sig = `${spec.fn}(to: string, ${spec.requiredToken}: string): void`;
|
|
191
|
-
subjectExpr = asLit(eff.subject);
|
|
192
|
-
sets = [`v.set(${asLit(spec.requiredToken)}, ${spec.requiredToken});`];
|
|
193
|
-
}
|
|
194
|
-
return [
|
|
195
|
-
` export function ${sig} {`,
|
|
196
|
-
` const T: string = ${asLit(eff.text)};`,
|
|
197
|
-
` const H: string = ${asLit(eff.html)};`,
|
|
198
|
-
` const v = new Map<string, string>();`,
|
|
199
|
-
...sets.map((s) => ` ${s}`),
|
|
200
|
-
` new EmailTemplate(${subjectExpr}, T, H).sendDetached(to, v, ${asLit(spec.purpose)});`,
|
|
201
|
-
` }`,
|
|
202
|
-
];
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/** Codegen the ambient `AuthEmail` AssemblyScript module. */
|
|
206
|
-
function moduleSource(parts: Parts): string {
|
|
207
|
-
const out: string[] = [
|
|
208
|
-
'// GENERATED by toiljs from the built-in auth email templates -- DO NOT EDIT.',
|
|
209
|
-
'// Override any of these by adding emails/auth-confirm.tsx, emails/auth-reset.tsx,',
|
|
210
|
-
'// or emails/auth-2fa.tsx (see docs/auth/emails.md). `EmailTemplate` is a toiljs',
|
|
211
|
-
'// global (server/globals/email.ts); the detached send keeps auth constant-time.',
|
|
212
|
-
'',
|
|
213
|
-
'export namespace AuthEmail {',
|
|
214
|
-
];
|
|
215
|
-
for (const name of AUTH_NAMES) out.push(...emitFn(SPECS[name], parts[name]));
|
|
216
|
-
out.push('}');
|
|
217
|
-
return out.join('\n') + '\n';
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Best-effort removal of a stale copy from the pre-0.0.107 location
|
|
222
|
-
* (`node_modules/toiljs/server/globals/_auth_emails.ts`). Leaving it there would
|
|
223
|
-
* define a SECOND `AuthEmail` namespace alongside the new app-local one and break
|
|
224
|
-
* the compile with a duplicate symbol, so a project upgraded in place (without a
|
|
225
|
-
* fresh `npm i`) is cleaned up here. Never throws (a read-only store just skips).
|
|
226
|
-
*/
|
|
227
|
-
function removeLegacyModule(root: string): void {
|
|
228
|
-
try {
|
|
229
|
-
const legacy = path.join(root, 'node_modules', 'toiljs', 'server', 'globals', GENERATED_BASENAME);
|
|
230
|
-
if (fs.existsSync(legacy)) fs.rmSync(legacy);
|
|
231
|
-
} catch {
|
|
232
|
-
// read-only / missing: nothing to clean, not fatal
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* (Re)write `_auth_emails.ts` into the app-local `.toil/authlib` LIB dir so the
|
|
238
|
-
* built-in auth controller has its email senders, baking any reserved
|
|
239
|
-
* `emails/*.tsx` override over the default. Runs BEFORE the toilscript server
|
|
240
|
-
* build (like {@link renderEmails}) so the module is compiled in via `--lib`. A
|
|
241
|
-
* no-op that removes a stale module when auth is off or there is no server. Only
|
|
242
|
-
* spins up Vite when an override actually exists.
|
|
243
|
-
*/
|
|
244
|
-
export async function renderAuthEmails(cfg: ResolvedToilConfig, authOn: boolean): Promise<void> {
|
|
245
|
-
const generatedPath = path.join(cfg.root, AUTH_EMAILS_LIB_SUBDIR, GENERATED_BASENAME);
|
|
246
|
-
removeLegacyModule(cfg.root);
|
|
247
|
-
|
|
248
|
-
// No server to compile into, or auth off: ensure no stale module lingers.
|
|
249
|
-
const hasServer = fs.existsSync(path.join(cfg.root, 'toilconfig.json'));
|
|
250
|
-
if (!authOn || !hasServer) {
|
|
251
|
-
if (fs.existsSync(generatedPath)) fs.rmSync(generatedPath);
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const emailsDir = path.join(cfg.root, 'emails');
|
|
256
|
-
const overrides = reservedFilesIn(emailsDir);
|
|
257
|
-
|
|
258
|
-
// Start from every default, then render + fold in whatever the app overrides.
|
|
259
|
-
const parts = {} as Parts;
|
|
260
|
-
for (const name of AUTH_NAMES) {
|
|
261
|
-
const spec = SPECS[name];
|
|
262
|
-
parts[name] = { subject: spec.defaultSubject, text: spec.defaultText, html: spec.defaultHtml };
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (overrides.size > 0) {
|
|
266
|
-
const { renderToStaticMarkup } = await import('react-dom/server');
|
|
267
|
-
const server = await createServer({
|
|
268
|
-
...(await createViteConfig(cfg)),
|
|
269
|
-
server: { middlewareMode: true, hmr: false },
|
|
270
|
-
appType: 'custom',
|
|
271
|
-
logLevel: 'silent',
|
|
272
|
-
});
|
|
273
|
-
try {
|
|
274
|
-
for (const [name, file] of overrides) {
|
|
275
|
-
try {
|
|
276
|
-
const r = await renderEmailFile(
|
|
277
|
-
server,
|
|
278
|
-
emailsDir,
|
|
279
|
-
file,
|
|
280
|
-
renderToStaticMarkup as (el: unknown) => string,
|
|
281
|
-
);
|
|
282
|
-
if (r) parts[name] = effectiveFromOverride(name, SPECS[name], r);
|
|
283
|
-
else warn(`skipped ${file} (no default-exported component)`);
|
|
284
|
-
} catch (err) {
|
|
285
|
-
warn(`skipped ${file} (${err instanceof Error ? err.message : String(err)})`);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
} finally {
|
|
289
|
-
await server.close();
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const next = moduleSource(parts);
|
|
294
|
-
const prev = fs.existsSync(generatedPath) ? fs.readFileSync(generatedPath, 'utf8') : null;
|
|
295
|
-
if (prev === next) return; // no change: do not bump mtime (would retrigger the dev watcher)
|
|
296
|
-
fs.mkdirSync(path.dirname(generatedPath), { recursive: true });
|
|
297
|
-
fs.writeFileSync(generatedPath, next);
|
|
298
|
-
if (overrides.size > 0)
|
|
299
|
-
process.stdout.write(
|
|
300
|
-
pc.green(' ✓ ') +
|
|
301
|
-
pc.dim(
|
|
302
|
-
`auth emails: overrode ${[...overrides.keys()].map((n) => SPECS[n].fn).join(', ')}`,
|
|
303
|
-
) +
|
|
304
|
-
'\n',
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/** The generated module's basename, so the server watcher can ignore its own output. */
|
|
309
|
-
export const AUTH_EMAILS_GENERATED_BASENAME = GENERATED_BASENAME;
|
|
310
|
-
|
|
311
|
-
// Exported for unit testing the pure fold/codegen without a Vite server.
|
|
312
|
-
export const __test = { moduleSource, effectiveFromOverride, SPECS };
|