toiljs 0.0.54 → 0.0.56
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/build/backend/.tsbuildinfo +1 -1
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/index.js +9 -5
- package/build/client/.tsbuildinfo +1 -1
- package/build/client/auth.js +1 -1
- package/build/client/components/Image.d.ts +1 -1
- package/build/client/dev/devtools.js +3 -1
- package/build/client/index.d.ts +2 -2
- package/build/client/index.js +2 -2
- package/build/client/routing/Router.js +1 -1
- package/build/client/routing/mount.js +1 -1
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/docs.js +1 -1
- package/build/compiler/seo.js +1 -3
- package/build/compiler/template-build.js +1 -1
- package/build/devserver/.tsbuildinfo +1 -1
- package/build/devserver/cache.js +0 -0
- package/build/devserver/crypto.js +45 -17
- package/build/devserver/database.d.ts +8 -0
- package/build/devserver/database.js +416 -0
- package/build/devserver/email/caps.js +0 -0
- package/build/devserver/email/config.js +7 -2
- package/build/devserver/email/validate.js +1 -4
- package/build/devserver/host.d.ts +2 -0
- package/build/devserver/host.js +3 -2
- package/build/devserver/index.d.ts +1 -1
- package/build/devserver/index.js +3 -2
- package/build/devserver/module.js +52 -7
- package/build/devserver/proxy.js +2 -1
- package/build/io/.tsbuildinfo +1 -1
- package/build/io/codec.d.ts +5 -5
- package/build/io/codec.js +193 -77
- package/examples/basic/client/components/HoneycombBackground.tsx +1 -1
- package/examples/basic/client/public/images/logo.svg +37 -34
- package/examples/basic/client/public/index.html +14 -14
- package/examples/basic/client/routes/auth.tsx +18 -10
- package/examples/basic/client/routes/cookies.tsx +15 -24
- package/examples/basic/client/routes/crypto.tsx +4 -5
- package/examples/basic/client/routes/features/template/template.tsx +1 -1
- package/examples/basic/client/routes/hello.tsx +1 -1
- package/examples/basic/client/routes/pq.tsx +14 -14
- package/examples/basic/client/routes/rest.tsx +50 -1
- package/examples/basic/client/styles/main.css +25 -22
- package/examples/basic/client/toil.tsx +1 -1
- package/examples/basic/server/README.md +8 -8
- package/examples/basic/server/core/AppHandler.ts +4 -7
- package/examples/basic/server/main.ts +1 -0
- package/examples/basic/server/models/GuestEntry.ts +12 -0
- package/examples/basic/server/models/GuestbookView.ts +10 -0
- package/examples/basic/server/models/NewMessage.ts +6 -0
- package/examples/basic/server/routes/Auth.ts +50 -106
- package/examples/basic/server/routes/EnvDemo.ts +9 -3
- package/examples/basic/server/routes/Guestbook.ts +62 -0
- package/package.json +2 -2
- package/server/globals/auth.ts +3 -3
- package/server/globals/twofactor.ts +2 -1
- package/server/runtime/http/securecookies.ts +3 -2
- package/src/backend/index.ts +4 -2
- package/src/cli/doctor.ts +10 -3
- package/src/cli/notify.ts +1 -6
- package/src/cli/ui.ts +3 -3
- package/src/cli/version-check.ts +5 -1
- package/src/client/auth.ts +33 -10
- package/src/client/components/Form.tsx +2 -2
- package/src/client/components/Image.tsx +1 -1
- package/src/client/components/Script.tsx +1 -1
- package/src/client/components/Slot.tsx +1 -1
- package/src/client/dev/devtools.tsx +121 -54
- package/src/client/dev/error-overlay.tsx +7 -1
- package/src/client/head/metadata.ts +1 -1
- package/src/client/index.ts +13 -2
- package/src/client/routing/Router.tsx +2 -2
- package/src/client/routing/error-boundary.tsx +1 -1
- package/src/client/routing/loader.ts +2 -2
- package/src/client/routing/mount.tsx +5 -6
- package/src/compiler/docs.ts +1 -1
- package/src/compiler/email-preview.ts +1 -1
- package/src/compiler/generate.ts +1 -1
- package/src/compiler/seo.ts +1 -3
- package/src/compiler/ssg.ts +10 -4
- package/src/compiler/template-build.ts +2 -7
- package/src/compiler/template.ts +1 -4
- package/src/compiler/vite.ts +1 -1
- package/src/devserver/cache.ts +0 -0
- package/src/devserver/crypto.ts +140 -51
- package/src/devserver/database.ts +600 -0
- package/src/devserver/dotenv.ts +10 -2
- package/src/devserver/email/caps.ts +0 -0
- package/src/devserver/email/config.ts +8 -2
- package/src/devserver/email/index.ts +3 -3
- package/src/devserver/email/validate.ts +1 -4
- package/src/devserver/envelope.ts +3 -3
- package/src/devserver/host.ts +22 -9
- package/src/devserver/index.ts +15 -6
- package/src/devserver/module.ts +59 -11
- package/src/devserver/proxy.ts +5 -7
- package/src/io/codec.ts +226 -83
- package/test/devserver-database.test.ts +364 -0
- package/test/devserver-pqauth.test.ts +5 -65
- package/test/example-guestbook.test.ts +78 -0
- package/test/pqauth-e2e.test.ts +6 -6
- package/build/devserver/kv.d.ts +0 -3
- package/build/devserver/kv.js +0 -53
- package/src/devserver/kv.ts +0 -93
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* It stays decoupled from the Router (it computes the current match itself via `matchRoute`) so it
|
|
8
8
|
* renders even when the app tree has crashed.
|
|
9
9
|
*/
|
|
10
|
-
import { useEffect, useState, useSyncExternalStore
|
|
10
|
+
import { type ReactNode, useEffect, useState, useSyncExternalStore } from 'react';
|
|
11
11
|
|
|
12
12
|
import { type DevError, getErrorLog, subscribeErrors } from './error-overlay.js';
|
|
13
13
|
import {
|
|
@@ -21,10 +21,10 @@ import {
|
|
|
21
21
|
import {
|
|
22
22
|
clearLoaderData,
|
|
23
23
|
inspectLoaderCache,
|
|
24
|
+
type LoaderCacheSnapshot,
|
|
24
25
|
loaderKey,
|
|
25
26
|
revalidate,
|
|
26
27
|
subscribeLoaderCache,
|
|
27
|
-
type LoaderCacheSnapshot,
|
|
28
28
|
} from '../routing/loader.js';
|
|
29
29
|
import { matchRoute } from '../routing/match.js';
|
|
30
30
|
import { getPages } from '../search/search.js';
|
|
@@ -52,10 +52,22 @@ function ToilLogo({ size = 16 }: { size?: number }): ReactNode {
|
|
|
52
52
|
x2="467.12"
|
|
53
53
|
y2="467.12"
|
|
54
54
|
gradientUnits="userSpaceOnUse">
|
|
55
|
-
<stop
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
<stop
|
|
56
|
+
offset="0"
|
|
57
|
+
stopColor="#6990ff"
|
|
58
|
+
/>
|
|
59
|
+
<stop
|
|
60
|
+
offset=".28"
|
|
61
|
+
stopColor="#521be0"
|
|
62
|
+
/>
|
|
63
|
+
<stop
|
|
64
|
+
offset=".66"
|
|
65
|
+
stopColor="#6900f4"
|
|
66
|
+
/>
|
|
67
|
+
<stop
|
|
68
|
+
offset="1"
|
|
69
|
+
stopColor="#7f00f6"
|
|
70
|
+
/>
|
|
59
71
|
</linearGradient>
|
|
60
72
|
<linearGradient
|
|
61
73
|
id="toilDtB"
|
|
@@ -64,12 +76,28 @@ function ToilLogo({ size = 16 }: { size?: number }): ReactNode {
|
|
|
64
76
|
x2="149.99"
|
|
65
77
|
y2="0"
|
|
66
78
|
gradientUnits="userSpaceOnUse">
|
|
67
|
-
<stop
|
|
68
|
-
|
|
79
|
+
<stop
|
|
80
|
+
offset=".15"
|
|
81
|
+
stopColor="#6990ff"
|
|
82
|
+
stopOpacity=".6"
|
|
83
|
+
/>
|
|
84
|
+
<stop
|
|
85
|
+
offset=".55"
|
|
86
|
+
stopColor="#531ae1"
|
|
87
|
+
/>
|
|
69
88
|
</linearGradient>
|
|
70
89
|
</defs>
|
|
71
|
-
<rect
|
|
72
|
-
|
|
90
|
+
<rect
|
|
91
|
+
width="500"
|
|
92
|
+
height="500"
|
|
93
|
+
rx="130"
|
|
94
|
+
ry="130"
|
|
95
|
+
fill="url(#toilDtA)"
|
|
96
|
+
/>
|
|
97
|
+
<path
|
|
98
|
+
d="M299.98,0L0,355.49v-225.49C0,58.2,58.2,0,130,0h169.98Z"
|
|
99
|
+
fill="url(#toilDtB)"
|
|
100
|
+
/>
|
|
73
101
|
<path
|
|
74
102
|
d="M106.17,111.11h285.24c9.9,0,16.7,9.96,13.09,19.18l-17.98,45.96c-2.11,5.39-7.31,8.94-13.09,8.94h-74.65c-7.76,0-14.06,6.29-14.06,14.06v214.94c0,7.76-6.29,14.06-14.06,14.06h-45.96c-7.76,0-14.06-6.29-14.06-14.06v-217.25c0-7.76-6.29-14.06-14.06-14.06h-73.66c-5.82,0-11.04-3.59-13.12-9.02l-16.76-43.64c-3.54-9.21,3.26-19.1,13.12-19.1Z"
|
|
75
103
|
fill="#fff"
|
|
@@ -194,7 +222,10 @@ function safeJson(value: unknown): string {
|
|
|
194
222
|
}
|
|
195
223
|
|
|
196
224
|
/** Reads the current document head's meta + link tags (live). */
|
|
197
|
-
function readHead(): {
|
|
225
|
+
function readHead(): {
|
|
226
|
+
metas: { name: string; content: string }[];
|
|
227
|
+
links: { rel: string; href: string }[];
|
|
228
|
+
} {
|
|
198
229
|
const metas: { name: string; content: string }[] = [];
|
|
199
230
|
const links: { rel: string; href: string }[] = [];
|
|
200
231
|
if (typeof document === 'undefined') return { metas, links };
|
|
@@ -346,7 +377,9 @@ function RouteTab({
|
|
|
346
377
|
<Row k="slots">{activeSlots.length ? activeSlots.join(', ') : 'none'}</Row>
|
|
347
378
|
<Row k="navigating">{pending ? 'yes' : 'no'}</Row>
|
|
348
379
|
|
|
349
|
-
<p
|
|
380
|
+
<p
|
|
381
|
+
className="toil-dt-sec"
|
|
382
|
+
style={{ marginTop: 12 }}>
|
|
350
383
|
Routes ({routes.length})
|
|
351
384
|
</p>
|
|
352
385
|
{routes.map((r) => {
|
|
@@ -474,7 +507,9 @@ function HeadTab(): ReactNode {
|
|
|
474
507
|
<div className="toil-dt-body">
|
|
475
508
|
<Row k="title">{title || '(none)'}</Row>
|
|
476
509
|
|
|
477
|
-
<p
|
|
510
|
+
<p
|
|
511
|
+
className="toil-dt-sec"
|
|
512
|
+
style={{ marginTop: 10 }}>
|
|
478
513
|
OpenGraph preview
|
|
479
514
|
</p>
|
|
480
515
|
<div className="toil-dt-og">
|
|
@@ -491,23 +526,41 @@ function HeadTab(): ReactNode {
|
|
|
491
526
|
</div>
|
|
492
527
|
</div>
|
|
493
528
|
|
|
494
|
-
<p
|
|
529
|
+
<p
|
|
530
|
+
className="toil-dt-sec"
|
|
531
|
+
style={{ marginTop: 10 }}>
|
|
495
532
|
SEO checklist
|
|
496
533
|
</p>
|
|
497
|
-
<Check
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
534
|
+
<Check
|
|
535
|
+
ok={Boolean(title)}
|
|
536
|
+
label="Has a title"
|
|
537
|
+
/>
|
|
538
|
+
<Check
|
|
539
|
+
ok={meta('description') !== undefined}
|
|
540
|
+
label="Has a meta description"
|
|
541
|
+
/>
|
|
542
|
+
<Check
|
|
543
|
+
ok={og.image !== undefined}
|
|
544
|
+
label="Has an og:image"
|
|
545
|
+
/>
|
|
546
|
+
<Check
|
|
547
|
+
ok={links.some((l) => l.rel === 'canonical')}
|
|
548
|
+
label="Has a canonical link"
|
|
549
|
+
/>
|
|
501
550
|
<Check
|
|
502
551
|
ok={pages.length === 0 || described === pages.length}
|
|
503
552
|
label={`Pages with a description: ${String(described)}/${String(pages.length)}`}
|
|
504
553
|
/>
|
|
505
554
|
|
|
506
|
-
<p
|
|
555
|
+
<p
|
|
556
|
+
className="toil-dt-sec"
|
|
557
|
+
style={{ marginTop: 10 }}>
|
|
507
558
|
Meta ({metas.length})
|
|
508
559
|
</p>
|
|
509
560
|
{metas.map((m, i) => (
|
|
510
|
-
<Row
|
|
561
|
+
<Row
|
|
562
|
+
k={m.name}
|
|
563
|
+
key={`${m.name}:${String(i)}`}>
|
|
511
564
|
{m.content}
|
|
512
565
|
</Row>
|
|
513
566
|
))}
|
|
@@ -546,7 +599,8 @@ function BuildTab({ info }: { info: DevInfo | null }): ReactNode {
|
|
|
546
599
|
|
|
547
600
|
function ErrorsTab(): ReactNode {
|
|
548
601
|
const errors = useErrors();
|
|
549
|
-
if (errors.length === 0)
|
|
602
|
+
if (errors.length === 0)
|
|
603
|
+
return <p className="toil-dt-empty toil-dt-body">No errors captured.</p>;
|
|
550
604
|
return (
|
|
551
605
|
<div className="toil-dt-body">
|
|
552
606
|
{[...errors].reverse().map((e, i) => (
|
|
@@ -974,45 +1028,58 @@ export function DevToolbar({
|
|
|
974
1028
|
|
|
975
1029
|
return (
|
|
976
1030
|
<>
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
<button
|
|
986
|
-
className="toil-dt-x"
|
|
987
|
-
onClick={() => {
|
|
988
|
-
setPrefs({ open: false });
|
|
989
|
-
}}>
|
|
990
|
-
✕
|
|
991
|
-
</button>
|
|
992
|
-
</div>
|
|
993
|
-
<div className="toil-dt-tabs">
|
|
994
|
-
{TABS.map((t) => (
|
|
1031
|
+
<div className={`toil-dt ${p.side}`}>
|
|
1032
|
+
<div className="toil-dt-panel">
|
|
1033
|
+
<div className="toil-dt-head">
|
|
1034
|
+
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
1035
|
+
<ToilLogo size={14} />
|
|
1036
|
+
<span className="toil-dt-logo">toiljs</span> devtools
|
|
1037
|
+
<span className={`toil-dt-dot ${dotClass}`} />
|
|
1038
|
+
</span>
|
|
995
1039
|
<button
|
|
996
|
-
|
|
997
|
-
className={`toil-dt-tab ${p.tab === t.id ? 'active' : ''}`}
|
|
1040
|
+
className="toil-dt-x"
|
|
998
1041
|
onClick={() => {
|
|
999
|
-
setPrefs({
|
|
1042
|
+
setPrefs({ open: false });
|
|
1000
1043
|
}}>
|
|
1001
|
-
|
|
1002
|
-
{t.id === 'errors' && errors.length > 0 ? ` (${String(errors.length)})` : ''}
|
|
1044
|
+
✕
|
|
1003
1045
|
</button>
|
|
1004
|
-
|
|
1046
|
+
</div>
|
|
1047
|
+
<div className="toil-dt-tabs">
|
|
1048
|
+
{TABS.map((t) => (
|
|
1049
|
+
<button
|
|
1050
|
+
key={t.id}
|
|
1051
|
+
className={`toil-dt-tab ${p.tab === t.id ? 'active' : ''}`}
|
|
1052
|
+
onClick={() => {
|
|
1053
|
+
setPrefs({ tab: t.id });
|
|
1054
|
+
}}>
|
|
1055
|
+
{t.label}
|
|
1056
|
+
{t.id === 'errors' && errors.length > 0
|
|
1057
|
+
? ` (${String(errors.length)})`
|
|
1058
|
+
: ''}
|
|
1059
|
+
</button>
|
|
1060
|
+
))}
|
|
1061
|
+
</div>
|
|
1062
|
+
{p.tab === 'route' && (
|
|
1063
|
+
<RouteTab
|
|
1064
|
+
routes={routes}
|
|
1065
|
+
slots={slots}
|
|
1066
|
+
info={info}
|
|
1067
|
+
/>
|
|
1068
|
+
)}
|
|
1069
|
+
{p.tab === 'data' && <DataTab />}
|
|
1070
|
+
{p.tab === 'head' && <HeadTab />}
|
|
1071
|
+
{p.tab === 'build' && <BuildTab info={info} />}
|
|
1072
|
+
{p.tab === 'errors' && <ErrorsTab />}
|
|
1073
|
+
{p.tab === 'ai' && (
|
|
1074
|
+
<AiTab
|
|
1075
|
+
info={info}
|
|
1076
|
+
routes={routes}
|
|
1077
|
+
/>
|
|
1078
|
+
)}
|
|
1079
|
+
{p.tab === 'prefs' && <PrefsTab />}
|
|
1005
1080
|
</div>
|
|
1006
|
-
{p.tab === 'route' && <RouteTab routes={routes} slots={slots} info={info} />}
|
|
1007
|
-
{p.tab === 'data' && <DataTab />}
|
|
1008
|
-
{p.tab === 'head' && <HeadTab />}
|
|
1009
|
-
{p.tab === 'build' && <BuildTab info={info} />}
|
|
1010
|
-
{p.tab === 'errors' && <ErrorsTab />}
|
|
1011
|
-
{p.tab === 'ai' && <AiTab info={info} routes={routes} />}
|
|
1012
|
-
{p.tab === 'prefs' && <PrefsTab />}
|
|
1013
1081
|
</div>
|
|
1014
|
-
|
|
1015
|
-
{pal}
|
|
1082
|
+
{pal}
|
|
1016
1083
|
</>
|
|
1017
1084
|
);
|
|
1018
1085
|
}
|
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
* plus `window` `error` / `unhandledrejection` events. Shows the message, stack, and (for render
|
|
5
5
|
* errors) the React component stack, with Dismiss / Reload. Inert in production builds.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
Component,
|
|
9
|
+
type CSSProperties,
|
|
10
|
+
type ErrorInfo,
|
|
11
|
+
type ReactNode,
|
|
12
|
+
useSyncExternalStore,
|
|
13
|
+
} from 'react';
|
|
8
14
|
|
|
9
15
|
/** A captured dev error. */
|
|
10
16
|
export interface DevError {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* data); the compiler-driven loader resolves it to a {@link HeadSpec} that the router applies as the
|
|
5
5
|
* route's baseline head (component-level `useHead`/`<Head>` still compose on top and can override).
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { type HeadSpec, type LinkTag, type MetaTag, useHead } from './head.js';
|
|
8
8
|
import type { RouteParams } from '../routing/match.js';
|
|
9
9
|
|
|
10
10
|
/** OpenGraph fields, expanded to `og:*` meta tags. */
|
package/src/client/index.ts
CHANGED
|
@@ -10,7 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
export { mount } from './routing/mount.js';
|
|
12
12
|
export { Router } from './routing/Router.js';
|
|
13
|
-
export {
|
|
13
|
+
export {
|
|
14
|
+
Auth,
|
|
15
|
+
register as authRegister,
|
|
16
|
+
login as authLogin,
|
|
17
|
+
buildLoginMessage,
|
|
18
|
+
LOGIN_CONTEXT,
|
|
19
|
+
} from './auth.js';
|
|
14
20
|
export type { KdfParams, AuthOptions } from './auth.js';
|
|
15
21
|
export { Link } from './navigation/Link.js';
|
|
16
22
|
export type { LinkProps } from './navigation/Link.js';
|
|
@@ -36,7 +42,12 @@ export {
|
|
|
36
42
|
useNavigationPending,
|
|
37
43
|
} from './routing/hooks.js';
|
|
38
44
|
export type { RouterInstance } from './routing/hooks.js';
|
|
39
|
-
export {
|
|
45
|
+
export {
|
|
46
|
+
useLoaderData,
|
|
47
|
+
revalidate,
|
|
48
|
+
invalidateLoaderData,
|
|
49
|
+
LoaderDataContext,
|
|
50
|
+
} from './routing/loader.js';
|
|
40
51
|
export type {
|
|
41
52
|
LoaderArgs,
|
|
42
53
|
LoaderFunction,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createElement, Suspense, useLayoutEffect
|
|
1
|
+
import { createElement, type ReactNode, Suspense, useLayoutEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
import { ErrorBoundary } from './error-boundary.js';
|
|
4
4
|
import { useLocation } from './hooks.js';
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
resolveLayout,
|
|
10
10
|
resolveNotFound,
|
|
11
11
|
} from './lazy.js';
|
|
12
|
-
import {
|
|
12
|
+
import { LoaderDataContext, loaderKey, readRouteData } from './loader.js';
|
|
13
13
|
import { matchRoute, type RouteParams } from './match.js';
|
|
14
14
|
import { useRouteHead } from '../head/head.js';
|
|
15
15
|
import { ParamsContext } from './params-context.js';
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* a number keeps data fresh for that many seconds; `false` caches until manual invalidation.
|
|
11
11
|
* `revalidate()` / `router.refresh()` bust the cache to force a refetch.
|
|
12
12
|
*/
|
|
13
|
-
import { createContext, useContext
|
|
13
|
+
import { type ComponentType, createContext, useContext } from 'react';
|
|
14
14
|
|
|
15
15
|
import type { HeadSpec } from '../head/head.js';
|
|
16
|
-
import {
|
|
16
|
+
import { type GenerateMetadata, type Metadata, resolveMetadata } from '../head/metadata.js';
|
|
17
17
|
import { navigationEpoch, refresh as rerender } from '../navigation/navigation.js';
|
|
18
18
|
import type { RouteDef } from '../types.js';
|
|
19
19
|
import type { RouteParams } from './match.js';
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { createRoot, hydrateRoot } from 'react-dom/client';
|
|
2
2
|
|
|
3
3
|
import { DevToolbar } from '../dev/devtools.js';
|
|
4
|
-
import {
|
|
5
|
-
DevErrorBoundary,
|
|
6
|
-
DevErrorOverlay,
|
|
7
|
-
initDevErrorOverlay,
|
|
8
|
-
} from '../dev/error-overlay.js';
|
|
4
|
+
import { DevErrorBoundary, DevErrorOverlay, initDevErrorOverlay } from '../dev/error-overlay.js';
|
|
9
5
|
import { initNavigation } from '../navigation/navigation.js';
|
|
10
6
|
import { startPrefetcher } from '../navigation/prefetch.js';
|
|
11
7
|
import { hydrateLoaderData } from './loader.js';
|
|
@@ -73,7 +69,10 @@ export function mount(
|
|
|
73
69
|
<>
|
|
74
70
|
<DevErrorBoundary>{app}</DevErrorBoundary>
|
|
75
71
|
<DevErrorOverlay />
|
|
76
|
-
<DevToolbar
|
|
72
|
+
<DevToolbar
|
|
73
|
+
routes={routes}
|
|
74
|
+
slots={slots}
|
|
75
|
+
/>
|
|
77
76
|
</>,
|
|
78
77
|
);
|
|
79
78
|
} else if (isSsrDocument()) {
|
package/src/compiler/docs.ts
CHANGED
|
@@ -346,7 +346,7 @@ export const TOIL_DOCS: Record<string, string> = {
|
|
|
346
346
|
' an `<Island>`. A route that cannot render this way is skipped at build (with a warning) and',
|
|
347
347
|
' simply falls back to normal client rendering.',
|
|
348
348
|
'- Hole values are HTML-escaped exactly as React escapes them, so hydration is byte-for-byte',
|
|
349
|
-
|
|
349
|
+
" clean. Keep a repeat row's structure the same across items (only the leaf hole values vary).",
|
|
350
350
|
'- Build output for an SSR route lands in `build/client/_ssr/` (the template + its manifest)',
|
|
351
351
|
' alongside the generated `Slot` module; routes without `ssr = true` are unaffected.',
|
|
352
352
|
]),
|
|
@@ -13,7 +13,7 @@ import path from 'node:path';
|
|
|
13
13
|
import type { ViteDevServer } from 'vite';
|
|
14
14
|
|
|
15
15
|
import type { ResolvedToilConfig } from './config.js';
|
|
16
|
-
import { renderEmailFile, toPascal
|
|
16
|
+
import { type RenderedEmail, renderEmailFile, toPascal } from './emails.js';
|
|
17
17
|
|
|
18
18
|
/** One discoverable email: its generated `Emails.<name>` and its absolute file. */
|
|
19
19
|
export interface EmailListItem {
|
package/src/compiler/generate.ts
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { type ResolvedToilConfig } from './config.js';
|
|
5
5
|
import { writeDocs } from './docs.js';
|
|
6
6
|
import { buildPageIndex, pagesModuleSource } from './pages.js';
|
|
7
|
-
import {
|
|
7
|
+
import { type ScannedRoute, scanRoutes } from './routes.js';
|
|
8
8
|
import { llmsTxt, robotsTxt, sitemapXml } from './seo.js';
|
|
9
9
|
|
|
10
10
|
/**
|
package/src/compiler/seo.ts
CHANGED
|
@@ -407,9 +407,7 @@ export function llmsTxt(
|
|
|
407
407
|
const title = escapeMarkdownInline(page.title);
|
|
408
408
|
const url = escapeMarkdownUrl(page.url);
|
|
409
409
|
const desc =
|
|
410
|
-
page.description !== undefined
|
|
411
|
-
? `: ${escapeMarkdownInline(page.description)}`
|
|
412
|
-
: '';
|
|
410
|
+
page.description !== undefined ? `: ${escapeMarkdownInline(page.description)}` : '';
|
|
413
411
|
out.push(`- [${title}](${url})${desc}`);
|
|
414
412
|
}
|
|
415
413
|
}
|
package/src/compiler/ssg.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { createServer } from 'vite';
|
|
|
16
16
|
import { type ResolvedToilConfig } from './config.js';
|
|
17
17
|
import { extractStaticMetadata, loadTypeScript } from './prerender.js';
|
|
18
18
|
import { scanRoutes } from './routes.js';
|
|
19
|
-
import { injectSeoHtml, joinUrl, llmsTxt, routeSeo, sitemapXml
|
|
19
|
+
import { injectSeoHtml, joinUrl, type LlmsPage, llmsTxt, routeSeo, sitemapXml } from './seo.js';
|
|
20
20
|
import { createViteConfig } from './vite.js';
|
|
21
21
|
|
|
22
22
|
/** Reads a string field off a metadata record, or undefined. */
|
|
@@ -91,7 +91,9 @@ export async function prerenderStaticParams(cfg: ResolvedToilConfig): Promise<st
|
|
|
91
91
|
try {
|
|
92
92
|
mod = (await server.ssrLoadModule(route.file)) as RouteModule;
|
|
93
93
|
} catch (err) {
|
|
94
|
-
warn(
|
|
94
|
+
warn(
|
|
95
|
+
`skipped ${route.pattern} (${err instanceof Error ? err.message : String(err)})`,
|
|
96
|
+
);
|
|
95
97
|
continue;
|
|
96
98
|
}
|
|
97
99
|
if (typeof mod.generateStaticParams !== 'function') continue;
|
|
@@ -116,12 +118,16 @@ export async function prerenderStaticParams(cfg: ResolvedToilConfig): Promise<st
|
|
|
116
118
|
typeof mod.loader === 'function'
|
|
117
119
|
? await mod.loader({ params, searchParams })
|
|
118
120
|
: undefined;
|
|
119
|
-
metadata = asMetadata(
|
|
121
|
+
metadata = asMetadata(
|
|
122
|
+
await mod.generateMetadata({ params, searchParams, data }),
|
|
123
|
+
);
|
|
120
124
|
} else if (mod.metadata) {
|
|
121
125
|
metadata = asMetadata(mod.metadata);
|
|
122
126
|
}
|
|
123
127
|
} catch (err) {
|
|
124
|
-
warn(
|
|
128
|
+
warn(
|
|
129
|
+
`metadata failed for ${url} (${err instanceof Error ? err.message : String(err)})`,
|
|
130
|
+
);
|
|
125
131
|
}
|
|
126
132
|
const html = injectSeoHtml(shell, routeSeo(cfg.seo, metadata, url));
|
|
127
133
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
@@ -19,12 +19,7 @@
|
|
|
19
19
|
import fs from 'node:fs';
|
|
20
20
|
import path from 'node:path';
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
createElement,
|
|
24
|
-
type ComponentType,
|
|
25
|
-
type Context,
|
|
26
|
-
type ReactNode,
|
|
27
|
-
} from 'react';
|
|
22
|
+
import { type ComponentType, type Context, createElement, type ReactNode } from 'react';
|
|
28
23
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|
29
24
|
import { createServer } from 'vite';
|
|
30
25
|
|
|
@@ -36,8 +31,8 @@ import {
|
|
|
36
31
|
assignSlotIds,
|
|
37
32
|
coherenceHash,
|
|
38
33
|
encodeSlots,
|
|
39
|
-
extractFromHtml,
|
|
40
34
|
type Extracted,
|
|
35
|
+
extractFromHtml,
|
|
41
36
|
} from './template.js';
|
|
42
37
|
import { createViteConfig } from './vite.js';
|
|
43
38
|
|
package/src/compiler/template.ts
CHANGED
|
@@ -249,10 +249,7 @@ export function reactEscapeHtml(s: string): string {
|
|
|
249
249
|
* any tooling that needs to materialise a full page from a template + values).
|
|
250
250
|
* `values` maps a byte offset to the bytes inserted there (offsets may repeat
|
|
251
251
|
* is not allowed; pass them in `slots` order). */
|
|
252
|
-
export function spliceTemplate(
|
|
253
|
-
tmpl: Buffer,
|
|
254
|
-
inserts: { offset: number; value: Buffer }[],
|
|
255
|
-
): Buffer {
|
|
252
|
+
export function spliceTemplate(tmpl: Buffer, inserts: { offset: number; value: Buffer }[]): Buffer {
|
|
256
253
|
const parts: Buffer[] = [];
|
|
257
254
|
let prev = 0;
|
|
258
255
|
for (const ins of inserts) {
|
package/src/compiler/vite.ts
CHANGED
|
@@ -7,7 +7,7 @@ import pc from 'picocolors';
|
|
|
7
7
|
import react from '@vitejs/plugin-react';
|
|
8
8
|
import { imagetools } from 'vite-imagetools';
|
|
9
9
|
import { nodePolyfills } from 'vite-plugin-node-polyfills';
|
|
10
|
-
import { createLogger,
|
|
10
|
+
import { createLogger, type InlineConfig, type Logger, mergeConfig, type PluginOption } from 'vite';
|
|
11
11
|
|
|
12
12
|
import { type ResolvedToilConfig } from './config.js';
|
|
13
13
|
import { fontPreloadPlugin } from './fonts.js';
|
package/src/devserver/cache.ts
CHANGED
|
Binary file
|