@payloadcms/tanstack-start 4.0.0-beta.0
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/LICENSE.md +22 -0
- package/dist/adapters/handleServerFunctions.d.ts +2 -0
- package/dist/adapters/handleServerFunctions.d.ts.map +1 -0
- package/dist/adapters/handleServerFunctions.js +9 -0
- package/dist/adapters/handleServerFunctions.js.map +1 -0
- package/dist/adapters/layout/index.d.ts +66 -0
- package/dist/adapters/layout/index.d.ts.map +1 -0
- package/dist/adapters/layout/index.js +105 -0
- package/dist/adapters/layout/index.js.map +1 -0
- package/dist/adapters/layout/lazy.d.ts +11 -0
- package/dist/adapters/layout/lazy.d.ts.map +1 -0
- package/dist/adapters/layout/lazy.js +23 -0
- package/dist/adapters/layout/lazy.js.map +1 -0
- package/dist/adapters/layout/server.d.ts +29 -0
- package/dist/adapters/layout/server.d.ts.map +1 -0
- package/dist/adapters/layout/server.js +63 -0
- package/dist/adapters/layout/server.js.map +1 -0
- package/dist/adapters/metadata.d.ts +80 -0
- package/dist/adapters/metadata.d.ts.map +1 -0
- package/dist/adapters/metadata.js +202 -0
- package/dist/adapters/metadata.js.map +1 -0
- package/dist/adapters/router.d.ts +3 -0
- package/dist/adapters/router.d.ts.map +1 -0
- package/dist/adapters/router.js +179 -0
- package/dist/adapters/router.js.map +1 -0
- package/dist/adapters/server.d.ts +36 -0
- package/dist/adapters/server.d.ts.map +1 -0
- package/dist/adapters/server.js +130 -0
- package/dist/adapters/server.js.map +1 -0
- package/dist/adapters/views/index.d.ts +104 -0
- package/dist/adapters/views/index.d.ts.map +1 -0
- package/dist/adapters/views/index.js +80 -0
- package/dist/adapters/views/index.js.map +1 -0
- package/dist/adapters/views/lazy.d.ts +4 -0
- package/dist/adapters/views/lazy.d.ts.map +1 -0
- package/dist/adapters/views/lazy.js +43 -0
- package/dist/adapters/views/lazy.js.map +1 -0
- package/dist/adapters/views/server.d.ts +57 -0
- package/dist/adapters/views/server.d.ts.map +1 -0
- package/dist/adapters/views/server.js +169 -0
- package/dist/adapters/views/server.js.map +1 -0
- package/dist/auth/login.d.ts +4 -0
- package/dist/auth/login.d.ts.map +1 -0
- package/dist/auth/login.js +10 -0
- package/dist/auth/login.js.map +1 -0
- package/dist/auth/logout.d.ts +6 -0
- package/dist/auth/logout.d.ts.map +1 -0
- package/dist/auth/logout.js +10 -0
- package/dist/auth/logout.js.map +1 -0
- package/dist/auth/refresh.d.ts +6 -0
- package/dist/auth/refresh.d.ts.map +1 -0
- package/dist/auth/refresh.js +10 -0
- package/dist/auth/refresh.js.map +1 -0
- package/dist/exports/client.d.ts +4 -0
- package/dist/exports/client.d.ts.map +1 -0
- package/dist/exports/client.js +6 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/layouts.d.ts +3 -0
- package/dist/exports/layouts.d.ts.map +1 -0
- package/dist/exports/layouts.js +3 -0
- package/dist/exports/layouts.js.map +1 -0
- package/dist/exports/rsc.browser.d.ts +15 -0
- package/dist/exports/rsc.browser.d.ts.map +1 -0
- package/dist/exports/rsc.browser.js +15 -0
- package/dist/exports/rsc.browser.js.map +1 -0
- package/dist/exports/rsc.d.ts +4 -0
- package/dist/exports/rsc.d.ts.map +1 -0
- package/dist/exports/rsc.js +5 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/exports/server.d.ts +15 -0
- package/dist/exports/server.d.ts.map +1 -0
- package/dist/exports/server.js +16 -0
- package/dist/exports/server.js.map +1 -0
- package/dist/exports/shared.d.ts +2 -0
- package/dist/exports/shared.d.ts.map +1 -0
- package/dist/exports/shared.js +3 -0
- package/dist/exports/shared.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/graphql/handler.server.d.ts +10 -0
- package/dist/routes/graphql/handler.server.d.ts.map +1 -0
- package/dist/routes/graphql/handler.server.js +132 -0
- package/dist/routes/graphql/handler.server.js.map +1 -0
- package/dist/routes/rest/handler.server.d.ts +11 -0
- package/dist/routes/rest/handler.server.d.ts.map +1 -0
- package/dist/routes/rest/handler.server.js +17 -0
- package/dist/routes/rest/handler.server.js.map +1 -0
- package/dist/routes/rest/index.d.ts +34 -0
- package/dist/routes/rest/index.d.ts.map +1 -0
- package/dist/routes/rest/index.js +43 -0
- package/dist/routes/rest/index.js.map +1 -0
- package/dist/rsc/CollectionCards.d.ts +4 -0
- package/dist/rsc/CollectionCards.d.ts.map +1 -0
- package/dist/rsc/CollectionCards.js +12 -0
- package/dist/rsc/CollectionCards.js.map +1 -0
- package/dist/rsc/HierarchyTypeFieldServer.d.ts +9 -0
- package/dist/rsc/HierarchyTypeFieldServer.d.ts.map +1 -0
- package/dist/rsc/HierarchyTypeFieldServer.js +35 -0
- package/dist/rsc/HierarchyTypeFieldServer.js.map +1 -0
- package/dist/rsc/renderOnDemand.d.ts +15 -0
- package/dist/rsc/renderOnDemand.d.ts.map +1 -0
- package/dist/rsc/renderOnDemand.js +25 -0
- package/dist/rsc/renderOnDemand.js.map +1 -0
- package/dist/utilities/devConfigReload.server.d.ts +2 -0
- package/dist/utilities/devConfigReload.server.d.ts.map +1 -0
- package/dist/utilities/devConfigReload.server.js +63 -0
- package/dist/utilities/devConfigReload.server.js.map +1 -0
- package/dist/utilities/devConfigReloadEvent.d.ts +9 -0
- package/dist/utilities/devConfigReloadEvent.d.ts.map +1 -0
- package/dist/utilities/devConfigReloadEvent.js +9 -0
- package/dist/utilities/devConfigReloadEvent.js.map +1 -0
- package/dist/utilities/getRequestI18n.server.d.ts +12 -0
- package/dist/utilities/getRequestI18n.server.d.ts.map +1 -0
- package/dist/utilities/getRequestI18n.server.js +25 -0
- package/dist/utilities/getRequestI18n.server.js.map +1 -0
- package/dist/utilities/initAdminContext.server.d.ts +9 -0
- package/dist/utilities/initAdminContext.server.d.ts.map +1 -0
- package/dist/utilities/initAdminContext.server.js +13 -0
- package/dist/utilities/initAdminContext.server.js.map +1 -0
- package/dist/utilities/routerSearch.d.ts +40 -0
- package/dist/utilities/routerSearch.d.ts.map +1 -0
- package/dist/utilities/routerSearch.js +45 -0
- package/dist/utilities/routerSearch.js.map +1 -0
- package/dist/utilities/serializeForRsc.d.ts +26 -0
- package/dist/utilities/serializeForRsc.d.ts.map +1 -0
- package/dist/utilities/serializeForRsc.js +127 -0
- package/dist/utilities/serializeForRsc.js.map +1 -0
- package/dist/utilities/serverFunctionClient.d.ts +29 -0
- package/dist/utilities/serverFunctionClient.d.ts.map +1 -0
- package/dist/utilities/serverFunctionClient.js +114 -0
- package/dist/utilities/serverFunctionClient.js.map +1 -0
- package/dist/utilities/toSerializable.d.ts +11 -0
- package/dist/utilities/toSerializable.d.ts.map +1 -0
- package/dist/utilities/toSerializable.js +127 -0
- package/dist/utilities/toSerializable.js.map +1 -0
- package/dist/withPayload/config/external.d.ts +44 -0
- package/dist/withPayload/config/external.d.ts.map +1 -0
- package/dist/withPayload/config/external.js +96 -0
- package/dist/withPayload/config/external.js.map +1 -0
- package/dist/withPayload/config/optimizeDeps.d.ts +25 -0
- package/dist/withPayload/config/optimizeDeps.d.ts.map +1 -0
- package/dist/withPayload/config/optimizeDeps.js +160 -0
- package/dist/withPayload/config/optimizeDeps.js.map +1 -0
- package/dist/withPayload/config/scss.d.ts +14 -0
- package/dist/withPayload/config/scss.d.ts.map +1 -0
- package/dist/withPayload/config/scss.js +28 -0
- package/dist/withPayload/config/scss.js.map +1 -0
- package/dist/withPayload/devConfigReload.d.ts +14 -0
- package/dist/withPayload/devConfigReload.d.ts.map +1 -0
- package/dist/withPayload/devConfigReload.js +84 -0
- package/dist/withPayload/devConfigReload.js.map +1 -0
- package/dist/withPayload/importProtection.d.ts +14 -0
- package/dist/withPayload/importProtection.d.ts.map +1 -0
- package/dist/withPayload/importProtection.js +54 -0
- package/dist/withPayload/importProtection.js.map +1 -0
- package/dist/withPayload/index.d.ts +110 -0
- package/dist/withPayload/index.d.ts.map +1 -0
- package/dist/withPayload/index.js +300 -0
- package/dist/withPayload/index.js.map +1 -0
- package/dist/withPayload/workarounds/clientModuleResolution.d.ts +14 -0
- package/dist/withPayload/workarounds/clientModuleResolution.d.ts.map +1 -0
- package/dist/withPayload/workarounds/clientModuleResolution.js +98 -0
- package/dist/withPayload/workarounds/clientModuleResolution.js.map +1 -0
- package/dist/withPayload/workarounds/devTransforms.d.ts +19 -0
- package/dist/withPayload/workarounds/devTransforms.d.ts.map +1 -0
- package/dist/withPayload/workarounds/devTransforms.js +72 -0
- package/dist/withPayload/workarounds/devTransforms.js.map +1 -0
- package/dist/withPayload/workarounds/reactDomServerInRsc.d.ts +27 -0
- package/dist/withPayload/workarounds/reactDomServerInRsc.d.ts.map +1 -0
- package/dist/withPayload/workarounds/reactDomServerInRsc.js +90 -0
- package/dist/withPayload/workarounds/reactDomServerInRsc.js.map +1 -0
- package/dist/withPayload/workarounds/stripDistStyleImports.d.ts +21 -0
- package/dist/withPayload/workarounds/stripDistStyleImports.d.ts.map +1 -0
- package/dist/withPayload/workarounds/stripDistStyleImports.js +140 -0
- package/dist/withPayload/workarounds/stripDistStyleImports.js.map +1 -0
- package/dist/withPayload/workarounds/stubPrettierInClient.d.ts +13 -0
- package/dist/withPayload/workarounds/stubPrettierInClient.d.ts.map +1 -0
- package/dist/withPayload/workarounds/stubPrettierInClient.js +32 -0
- package/dist/withPayload/workarounds/stubPrettierInClient.js.map +1 -0
- package/dist/withPayload/workarounds/wrapCjsForClient.d.ts +15 -0
- package/dist/withPayload/workarounds/wrapCjsForClient.d.ts.map +1 -0
- package/dist/withPayload/workarounds/wrapCjsForClient.js +106 -0
- package/dist/withPayload/workarounds/wrapCjsForClient.js.map +1 -0
- package/package.json +114 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { getViewportContent } from '@payloadcms/ui/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Builds TanStack Router `head()` `meta` + `links` entries for an admin page
|
|
4
|
+
* from the resolved `AdminPageMetadata`, mirroring the tags Next.js renders
|
|
5
|
+
* natively from the same `MetaConfig` (title, description, robots, keywords,
|
|
6
|
+
* OpenGraph, Twitter — derived from OpenGraph — and icon links).
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* export const Route = createFileRoute('/admin/$')({
|
|
10
|
+
* head: ({ loaderData }) => getAdminMeta(loaderData?.metadata),
|
|
11
|
+
* })
|
|
12
|
+
* ```
|
|
13
|
+
*/ export function getAdminMeta(metadata) {
|
|
14
|
+
const meta = [
|
|
15
|
+
{
|
|
16
|
+
charSet: 'utf-8'
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'viewport',
|
|
20
|
+
content: metadata?.viewport ?? getViewportContent()
|
|
21
|
+
}
|
|
22
|
+
];
|
|
23
|
+
const links = [];
|
|
24
|
+
if (!metadata) {
|
|
25
|
+
return {
|
|
26
|
+
links,
|
|
27
|
+
meta
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (metadata.title) {
|
|
31
|
+
meta.push({
|
|
32
|
+
title: metadata.title
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if (metadata.description) {
|
|
36
|
+
meta.push({
|
|
37
|
+
name: 'description',
|
|
38
|
+
content: metadata.description
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (metadata.keywords) {
|
|
42
|
+
meta.push({
|
|
43
|
+
name: 'keywords',
|
|
44
|
+
content: metadata.keywords
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (metadata.robots) {
|
|
48
|
+
meta.push({
|
|
49
|
+
name: 'robots',
|
|
50
|
+
content: metadata.robots
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const og = metadata.openGraph;
|
|
54
|
+
if (og) {
|
|
55
|
+
if (og.title) {
|
|
56
|
+
meta.push({
|
|
57
|
+
content: og.title,
|
|
58
|
+
property: 'og:title'
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (og.description) {
|
|
62
|
+
meta.push({
|
|
63
|
+
content: og.description,
|
|
64
|
+
property: 'og:description'
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (og.siteName) {
|
|
68
|
+
meta.push({
|
|
69
|
+
content: og.siteName,
|
|
70
|
+
property: 'og:site_name'
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
for (const image of og.images ?? []){
|
|
74
|
+
meta.push({
|
|
75
|
+
content: image.url,
|
|
76
|
+
property: 'og:image'
|
|
77
|
+
});
|
|
78
|
+
if (image.width) {
|
|
79
|
+
meta.push({
|
|
80
|
+
content: String(image.width),
|
|
81
|
+
property: 'og:image:width'
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (image.height) {
|
|
85
|
+
meta.push({
|
|
86
|
+
content: String(image.height),
|
|
87
|
+
property: 'og:image:height'
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (image.alt) {
|
|
91
|
+
meta.push({
|
|
92
|
+
content: image.alt,
|
|
93
|
+
property: 'og:image:alt'
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Twitter card is inherited from OpenGraph (matches Next.js metadata resolution).
|
|
98
|
+
const firstImage = og.images?.[0];
|
|
99
|
+
if (firstImage) {
|
|
100
|
+
meta.push({
|
|
101
|
+
name: 'twitter:card',
|
|
102
|
+
content: 'summary_large_image'
|
|
103
|
+
});
|
|
104
|
+
meta.push({
|
|
105
|
+
name: 'twitter:image',
|
|
106
|
+
content: firstImage.url
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (og.title) {
|
|
110
|
+
meta.push({
|
|
111
|
+
name: 'twitter:title',
|
|
112
|
+
content: og.title
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (og.description) {
|
|
116
|
+
meta.push({
|
|
117
|
+
name: 'twitter:description',
|
|
118
|
+
content: og.description
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (const icon of metadata.icons ?? []){
|
|
123
|
+
links.push({
|
|
124
|
+
href: icon.url,
|
|
125
|
+
rel: icon.rel ?? 'icon',
|
|
126
|
+
...icon.media ? {
|
|
127
|
+
media: icon.media
|
|
128
|
+
} : {},
|
|
129
|
+
...icon.sizes ? {
|
|
130
|
+
sizes: icon.sizes
|
|
131
|
+
} : {},
|
|
132
|
+
...icon.type ? {
|
|
133
|
+
type: icon.type
|
|
134
|
+
} : {}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
links,
|
|
139
|
+
meta
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Flattens the framework-agnostic `MetaConfig` (Next.js `Metadata` shape) into
|
|
144
|
+
* the plain, serializable `AdminPageMetadata` the route loader ships to the
|
|
145
|
+
* client. The full `MetaConfig` carries a `URL` `metadataBase`, functions and
|
|
146
|
+
* other non-serializable values that seroval cannot cross the wire, so only the
|
|
147
|
+
* fields `getAdminMeta` renders are extracted.
|
|
148
|
+
*/ export const toAdminPageMetadata = (meta)=>{
|
|
149
|
+
const og = meta.openGraph;
|
|
150
|
+
const rawImages = og?.images;
|
|
151
|
+
const imagesArray = rawImages ? Array.isArray(rawImages) ? rawImages : [
|
|
152
|
+
rawImages
|
|
153
|
+
] : [];
|
|
154
|
+
const images = imagesArray.map((image)=>typeof image === 'string' ? {
|
|
155
|
+
url: image
|
|
156
|
+
} : image?.url ? {
|
|
157
|
+
alt: image.alt,
|
|
158
|
+
height: image.height,
|
|
159
|
+
url: String(image.url),
|
|
160
|
+
width: image.width
|
|
161
|
+
} : undefined).filter(Boolean);
|
|
162
|
+
const rawIcons = meta.icons;
|
|
163
|
+
const iconList = Array.isArray(rawIcons) ? rawIcons : rawIcons && typeof rawIcons === 'object' && Array.isArray(rawIcons.icon) ? rawIcons.icon : [];
|
|
164
|
+
const icons = iconList.map((icon)=>typeof icon === 'string' ? {
|
|
165
|
+
rel: 'icon',
|
|
166
|
+
url: icon
|
|
167
|
+
} : icon?.url ? {
|
|
168
|
+
type: icon.type,
|
|
169
|
+
media: icon.media,
|
|
170
|
+
rel: icon.rel ?? 'icon',
|
|
171
|
+
sizes: icon.sizes,
|
|
172
|
+
url: String(icon.url)
|
|
173
|
+
} : undefined).filter(Boolean);
|
|
174
|
+
const keywords = meta.keywords;
|
|
175
|
+
return {
|
|
176
|
+
description: typeof meta.description === 'string' ? meta.description : undefined,
|
|
177
|
+
icons: icons?.length ? icons : undefined,
|
|
178
|
+
keywords: typeof keywords === 'string' ? keywords : Array.isArray(keywords) ? keywords.join(', ') : undefined,
|
|
179
|
+
openGraph: og ? {
|
|
180
|
+
description: typeof og.description === 'string' ? og.description : undefined,
|
|
181
|
+
images: images?.length ? images : undefined,
|
|
182
|
+
siteName: typeof og.siteName === 'string' ? og.siteName : undefined,
|
|
183
|
+
title: typeof og.title === 'string' ? og.title : undefined
|
|
184
|
+
} : undefined,
|
|
185
|
+
robots: typeof meta.robots === 'string' ? meta.robots : undefined,
|
|
186
|
+
title: resolveTitle(meta.title)
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
const resolveTitle = (title)=>{
|
|
190
|
+
if (!title) {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
if (typeof title === 'string') {
|
|
194
|
+
return title;
|
|
195
|
+
}
|
|
196
|
+
if ('absolute' in title) {
|
|
197
|
+
return title.absolute;
|
|
198
|
+
}
|
|
199
|
+
return title.default;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/metadata.ts"],"sourcesContent":["import type { MetaConfig } from 'payload'\n\nimport { getViewportContent } from '@payloadcms/ui/shared'\n\ntype MetaEntry =\n | { charSet: string }\n | { content: string; name: string }\n | { content: string; property: string }\n | { title: string }\n\ntype LinkEntry = {\n href: string\n media?: string\n rel: string\n sizes?: string\n type?: string\n}\n\ntype AdminPageIcon = {\n media?: string\n rel?: string\n sizes?: string\n type?: string\n url: string\n}\n\ntype AdminPageOGImage = {\n alt?: string\n height?: number\n url: string\n width?: number\n}\n\n/**\n * Resolved admin-page metadata produced by the admin-page server function.\n *\n * The server function resolves the framework-agnostic `MetaConfig` (via the\n * shared `generatePageMetadata` in `@payloadcms/ui`) down to plain serializable\n * values, so the route loader can ship it across the wire and feed it to\n * `getAdminMeta` in `head()`.\n */\nexport type AdminPageMetadata = {\n description?: string\n icons?: AdminPageIcon[]\n keywords?: string\n openGraph?: {\n description?: string\n images?: AdminPageOGImage[]\n siteName?: string\n title?: string\n }\n robots?: string\n title?: string\n viewport?: string\n}\n\n/**\n * Builds TanStack Router `head()` `meta` + `links` entries for an admin page\n * from the resolved `AdminPageMetadata`, mirroring the tags Next.js renders\n * natively from the same `MetaConfig` (title, description, robots, keywords,\n * OpenGraph, Twitter — derived from OpenGraph — and icon links).\n *\n * ```ts\n * export const Route = createFileRoute('/admin/$')({\n * head: ({ loaderData }) => getAdminMeta(loaderData?.metadata),\n * })\n * ```\n */\nexport function getAdminMeta(metadata?: AdminPageMetadata): {\n links: LinkEntry[]\n meta: MetaEntry[]\n} {\n const meta: MetaEntry[] = [\n { charSet: 'utf-8' },\n { name: 'viewport', content: metadata?.viewport ?? getViewportContent() },\n ]\n const links: LinkEntry[] = []\n\n if (!metadata) {\n return { links, meta }\n }\n\n if (metadata.title) {\n meta.push({ title: metadata.title })\n }\n if (metadata.description) {\n meta.push({ name: 'description', content: metadata.description })\n }\n if (metadata.keywords) {\n meta.push({ name: 'keywords', content: metadata.keywords })\n }\n if (metadata.robots) {\n meta.push({ name: 'robots', content: metadata.robots })\n }\n\n const og = metadata.openGraph\n if (og) {\n if (og.title) {\n meta.push({ content: og.title, property: 'og:title' })\n }\n if (og.description) {\n meta.push({ content: og.description, property: 'og:description' })\n }\n if (og.siteName) {\n meta.push({ content: og.siteName, property: 'og:site_name' })\n }\n\n for (const image of og.images ?? []) {\n meta.push({ content: image.url, property: 'og:image' })\n if (image.width) {\n meta.push({ content: String(image.width), property: 'og:image:width' })\n }\n if (image.height) {\n meta.push({ content: String(image.height), property: 'og:image:height' })\n }\n if (image.alt) {\n meta.push({ content: image.alt, property: 'og:image:alt' })\n }\n }\n\n // Twitter card is inherited from OpenGraph (matches Next.js metadata resolution).\n const firstImage = og.images?.[0]\n if (firstImage) {\n meta.push({ name: 'twitter:card', content: 'summary_large_image' })\n meta.push({ name: 'twitter:image', content: firstImage.url })\n }\n if (og.title) {\n meta.push({ name: 'twitter:title', content: og.title })\n }\n if (og.description) {\n meta.push({ name: 'twitter:description', content: og.description })\n }\n }\n\n for (const icon of metadata.icons ?? []) {\n links.push({\n href: icon.url,\n rel: icon.rel ?? 'icon',\n ...(icon.media ? { media: icon.media } : {}),\n ...(icon.sizes ? { sizes: icon.sizes } : {}),\n ...(icon.type ? { type: icon.type } : {}),\n })\n }\n\n return { links, meta }\n}\n\n/**\n * Flattens the framework-agnostic `MetaConfig` (Next.js `Metadata` shape) into\n * the plain, serializable `AdminPageMetadata` the route loader ships to the\n * client. The full `MetaConfig` carries a `URL` `metadataBase`, functions and\n * other non-serializable values that seroval cannot cross the wire, so only the\n * fields `getAdminMeta` renders are extracted.\n */\nexport const toAdminPageMetadata = (meta: MetaConfig): AdminPageMetadata => {\n const og = meta.openGraph as\n | {\n description?: unknown\n images?: unknown\n siteName?: unknown\n title?: unknown\n }\n | undefined\n\n const rawImages = og?.images\n const imagesArray = rawImages ? (Array.isArray(rawImages) ? rawImages : [rawImages]) : []\n const images = imagesArray\n .map((image: any) =>\n typeof image === 'string'\n ? { url: image }\n : image?.url\n ? { alt: image.alt, height: image.height, url: String(image.url), width: image.width }\n : undefined,\n )\n .filter(Boolean) as NonNullable<AdminPageMetadata['openGraph']>['images']\n\n const rawIcons = meta.icons as any\n const iconList = Array.isArray(rawIcons)\n ? rawIcons\n : rawIcons && typeof rawIcons === 'object' && Array.isArray(rawIcons.icon)\n ? rawIcons.icon\n : []\n const icons = iconList\n .map((icon: any) =>\n typeof icon === 'string'\n ? { rel: 'icon', url: icon }\n : icon?.url\n ? {\n type: icon.type,\n media: icon.media,\n rel: icon.rel ?? 'icon',\n sizes: icon.sizes,\n url: String(icon.url),\n }\n : undefined,\n )\n .filter(Boolean) as AdminPageMetadata['icons']\n\n const keywords = meta.keywords\n\n return {\n description: typeof meta.description === 'string' ? meta.description : undefined,\n icons: icons?.length ? icons : undefined,\n keywords:\n typeof keywords === 'string'\n ? keywords\n : Array.isArray(keywords)\n ? keywords.join(', ')\n : undefined,\n openGraph: og\n ? {\n description: typeof og.description === 'string' ? og.description : undefined,\n images: images?.length ? images : undefined,\n siteName: typeof og.siteName === 'string' ? og.siteName : undefined,\n title: typeof og.title === 'string' ? og.title : undefined,\n }\n : undefined,\n robots: typeof meta.robots === 'string' ? meta.robots : undefined,\n title: resolveTitle(meta.title),\n }\n}\n\nconst resolveTitle = (title: MetaConfig['title']): string | undefined => {\n if (!title) {\n return undefined\n }\n if (typeof title === 'string') {\n return title\n }\n if ('absolute' in title) {\n return title.absolute\n }\n return title.default\n}\n"],"names":["getViewportContent","getAdminMeta","metadata","meta","charSet","name","content","viewport","links","title","push","description","keywords","robots","og","openGraph","property","siteName","image","images","url","width","String","height","alt","firstImage","icon","icons","href","rel","media","sizes","type","toAdminPageMetadata","rawImages","imagesArray","Array","isArray","map","undefined","filter","Boolean","rawIcons","iconList","length","join","resolveTitle","absolute","default"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/adapters/router.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAoB,sBAAsB,EAAE,MAAM,SAAS,CAAA;AA0EvE,eAAO,MAAM,qBAAqB,EAAE,sBAqHnC,CAAA"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { RouterAdapterContext, useRouteTransition } from '@payloadcms/ui';
|
|
4
|
+
import { createLink, useLocation, useParams, useRouter, useRouterState } from '@tanstack/react-router';
|
|
5
|
+
import * as qs from 'qs-esm';
|
|
6
|
+
import React, { useCallback, useEffect, useMemo } from 'react';
|
|
7
|
+
const TanStackLink = createLink(function Link({ ariaCurrent, children, ...props }) {
|
|
8
|
+
return /*#__PURE__*/ _jsx("a", {
|
|
9
|
+
...props,
|
|
10
|
+
"aria-current": ariaCurrent ?? props['aria-current'],
|
|
11
|
+
children: children
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
const normalizeNavigationTarget = ({ path, pathname, search })=>{
|
|
15
|
+
if (path.startsWith('http')) {
|
|
16
|
+
const url = new URL(path);
|
|
17
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
18
|
+
}
|
|
19
|
+
if (path.startsWith('?')) {
|
|
20
|
+
return `${pathname}${path}`;
|
|
21
|
+
}
|
|
22
|
+
if (path.startsWith('#')) {
|
|
23
|
+
return `${pathname}${search}${path}`;
|
|
24
|
+
}
|
|
25
|
+
return path;
|
|
26
|
+
};
|
|
27
|
+
const TanStackLinkAdapter = ({ children, href, prefetch, ref, replace, scroll, ...rest })=>{
|
|
28
|
+
return /*#__PURE__*/ _jsx(TanStackLink, {
|
|
29
|
+
ariaCurrent: rest['aria-current'],
|
|
30
|
+
preload: prefetch === false ? false : 'intent',
|
|
31
|
+
ref: ref,
|
|
32
|
+
replace: replace,
|
|
33
|
+
resetScroll: scroll,
|
|
34
|
+
to: href,
|
|
35
|
+
...rest,
|
|
36
|
+
children: children
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
export const TanStackRouterAdapter = ({ children })=>{
|
|
40
|
+
const router = useRouter();
|
|
41
|
+
const location = useLocation();
|
|
42
|
+
const params = useParams({
|
|
43
|
+
strict: false
|
|
44
|
+
});
|
|
45
|
+
const isRouterLoading = useRouterState({
|
|
46
|
+
select: (state)=>state.isLoading
|
|
47
|
+
});
|
|
48
|
+
const { holdRouteTransition } = useRouteTransition();
|
|
49
|
+
useEffect(()=>{
|
|
50
|
+
if (!isRouterLoading) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const releaseRouteTransition = holdRouteTransition();
|
|
54
|
+
return ()=>releaseRouteTransition();
|
|
55
|
+
}, [
|
|
56
|
+
holdRouteTransition,
|
|
57
|
+
isRouterLoading
|
|
58
|
+
]);
|
|
59
|
+
const adaptedParams = useMemo(()=>{
|
|
60
|
+
const adapted = {
|
|
61
|
+
...params
|
|
62
|
+
};
|
|
63
|
+
if ('_splat' in params && typeof params._splat === 'string') {
|
|
64
|
+
adapted.segments = params._splat.split('/').filter(Boolean);
|
|
65
|
+
}
|
|
66
|
+
return adapted;
|
|
67
|
+
}, [
|
|
68
|
+
params
|
|
69
|
+
]);
|
|
70
|
+
// Split a target into `to` (pathname) + a parsed `search` object, so the
|
|
71
|
+
// router serializes the query via its `stringifySearch` (qs, bracket-encoded)
|
|
72
|
+
// rather than embedding the raw string in `to`. Navigating with a string `to`
|
|
73
|
+
// leaves the search *unencoded* in `window.location.search`
|
|
74
|
+
// (e.g. `where[or][0]…`), which then never matches Payload's `qs.stringify`
|
|
75
|
+
// output (`where%5Bor%5D…`) — breaking the list-view `syncPropsToURL` guard,
|
|
76
|
+
// which compares the two and would otherwise clobber optimistic query state
|
|
77
|
+
// (e.g. an in-progress filter condition) on every navigation.
|
|
78
|
+
const toNavOptions = useCallback((path)=>{
|
|
79
|
+
const relativePath = normalizeNavigationTarget({
|
|
80
|
+
path,
|
|
81
|
+
pathname: window.location.pathname,
|
|
82
|
+
search: window.location.search
|
|
83
|
+
});
|
|
84
|
+
const queryIndex = relativePath.indexOf('?');
|
|
85
|
+
if (queryIndex === -1) {
|
|
86
|
+
return {
|
|
87
|
+
to: relativePath
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const searchObject = qs.parse(relativePath.slice(queryIndex + 1), {
|
|
91
|
+
depth: 10,
|
|
92
|
+
ignoreQueryPrefix: true
|
|
93
|
+
});
|
|
94
|
+
// Function form replaces the search entirely (no merge with current search).
|
|
95
|
+
return {
|
|
96
|
+
search: ()=>searchObject,
|
|
97
|
+
to: relativePath.slice(0, queryIndex)
|
|
98
|
+
};
|
|
99
|
+
}, []);
|
|
100
|
+
const back = useCallback(()=>router.history.back(), [
|
|
101
|
+
router
|
|
102
|
+
]);
|
|
103
|
+
const push = useCallback((path, options)=>{
|
|
104
|
+
void router.navigate({
|
|
105
|
+
...toNavOptions(path),
|
|
106
|
+
resetScroll: options?.scroll
|
|
107
|
+
});
|
|
108
|
+
}, [
|
|
109
|
+
router,
|
|
110
|
+
toNavOptions
|
|
111
|
+
]);
|
|
112
|
+
const refresh = useCallback(()=>{
|
|
113
|
+
void router.invalidate();
|
|
114
|
+
}, [
|
|
115
|
+
router
|
|
116
|
+
]);
|
|
117
|
+
const replace = useCallback((path, options)=>{
|
|
118
|
+
void router.navigate({
|
|
119
|
+
...toNavOptions(path),
|
|
120
|
+
replace: true,
|
|
121
|
+
resetScroll: options?.scroll
|
|
122
|
+
});
|
|
123
|
+
}, [
|
|
124
|
+
router,
|
|
125
|
+
toNavOptions
|
|
126
|
+
]);
|
|
127
|
+
// Mirror Next.js' router behavior to allow syncing client state to the URL without triggering a second server load.
|
|
128
|
+
// TanStack's browser history monkeypatches `window.history.replaceState` and notifies `router.load` on every call.
|
|
129
|
+
// Use `_ignoreSubscribers` to suppresses that notification — the same flag TanStack uses internally when flushing history.
|
|
130
|
+
const replaceState = useCallback((url)=>{
|
|
131
|
+
const { history } = router;
|
|
132
|
+
history._ignoreSubscribers = true;
|
|
133
|
+
try {
|
|
134
|
+
window.history.replaceState(null, '', url);
|
|
135
|
+
} finally{
|
|
136
|
+
history._ignoreSubscribers = false;
|
|
137
|
+
}
|
|
138
|
+
}, [
|
|
139
|
+
router
|
|
140
|
+
]);
|
|
141
|
+
const adaptedRouter = useMemo(()=>({
|
|
142
|
+
back,
|
|
143
|
+
push,
|
|
144
|
+
refresh,
|
|
145
|
+
replace,
|
|
146
|
+
replaceState
|
|
147
|
+
}), [
|
|
148
|
+
back,
|
|
149
|
+
push,
|
|
150
|
+
refresh,
|
|
151
|
+
replace,
|
|
152
|
+
replaceState
|
|
153
|
+
]);
|
|
154
|
+
// `location.searchStr` is the serialized query string; `location.search` is
|
|
155
|
+
// the parsed object (a nested structure once the router uses `qs` for search
|
|
156
|
+
// serialization), which `URLSearchParams` cannot consume. Build from the
|
|
157
|
+
// string so Payload's `parseSearchParams` re-parses the bracket notation.
|
|
158
|
+
const searchParams = useMemo(()=>new URLSearchParams(location.searchStr), [
|
|
159
|
+
location.searchStr
|
|
160
|
+
]);
|
|
161
|
+
const value = useMemo(()=>({
|
|
162
|
+
Link: TanStackLinkAdapter,
|
|
163
|
+
params: adaptedParams,
|
|
164
|
+
pathname: location.pathname,
|
|
165
|
+
router: adaptedRouter,
|
|
166
|
+
searchParams
|
|
167
|
+
}), [
|
|
168
|
+
adaptedParams,
|
|
169
|
+
location.pathname,
|
|
170
|
+
adaptedRouter,
|
|
171
|
+
searchParams
|
|
172
|
+
]);
|
|
173
|
+
return /*#__PURE__*/ _jsx(RouterAdapterContext, {
|
|
174
|
+
value: value,
|
|
175
|
+
children: children
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/router.tsx"],"sourcesContent":["'use client'\n\nimport type { RouterAdapterContextValue } from '@payloadcms/ui/providers/RouterAdapter'\nimport type { LinkAdapterProps, RouterAdapterComponent } from 'payload'\n\nimport { RouterAdapterContext, useRouteTransition } from '@payloadcms/ui'\nimport {\n createLink,\n useLocation,\n useParams,\n useRouter,\n useRouterState,\n} from '@tanstack/react-router'\nimport * as qs from 'qs-esm'\nimport React, { useCallback, useEffect, useMemo } from 'react'\n\ntype LinkProps = {\n ariaCurrent?: React.AriaAttributes['aria-current']\n} & React.ComponentPropsWithRef<'a'>\n\nconst TanStackLink = createLink(function Link({ ariaCurrent, children, ...props }: LinkProps) {\n return (\n <a {...props} aria-current={ariaCurrent ?? props['aria-current']}>\n {children}\n </a>\n )\n})\n\nconst normalizeNavigationTarget = ({\n path,\n pathname,\n search,\n}: {\n path: string\n pathname: string\n search: string\n}) => {\n if (path.startsWith('http')) {\n const url = new URL(path)\n return `${url.pathname}${url.search}${url.hash}`\n }\n\n if (path.startsWith('?')) {\n return `${pathname}${path}`\n }\n\n if (path.startsWith('#')) {\n return `${pathname}${search}${path}`\n }\n\n return path\n}\n\nconst TanStackLinkAdapter: React.FC<LinkAdapterProps> = ({\n children,\n href,\n prefetch,\n ref,\n replace,\n scroll,\n ...rest\n}) => {\n return (\n <TanStackLink\n ariaCurrent={rest['aria-current']}\n preload={prefetch === false ? false : 'intent'}\n ref={ref}\n replace={replace}\n resetScroll={scroll}\n to={href}\n {...rest}\n >\n {children}\n </TanStackLink>\n )\n}\n\nexport const TanStackRouterAdapter: RouterAdapterComponent = ({ children }) => {\n const router = useRouter()\n const location = useLocation()\n const params = useParams({ strict: false })\n const isRouterLoading = useRouterState({ select: (state) => state.isLoading })\n const { holdRouteTransition } = useRouteTransition()\n\n useEffect(() => {\n if (!isRouterLoading) {\n return\n }\n\n const releaseRouteTransition = holdRouteTransition()\n\n return () => releaseRouteTransition()\n }, [holdRouteTransition, isRouterLoading])\n\n const adaptedParams = useMemo(() => {\n const adapted: Record<string, string | string[]> = { ...params }\n\n if ('_splat' in params && typeof params._splat === 'string') {\n adapted.segments = params._splat.split('/').filter(Boolean)\n }\n\n return adapted\n }, [params])\n\n // Split a target into `to` (pathname) + a parsed `search` object, so the\n // router serializes the query via its `stringifySearch` (qs, bracket-encoded)\n // rather than embedding the raw string in `to`. Navigating with a string `to`\n // leaves the search *unencoded* in `window.location.search`\n // (e.g. `where[or][0]…`), which then never matches Payload's `qs.stringify`\n // output (`where%5Bor%5D…`) — breaking the list-view `syncPropsToURL` guard,\n // which compares the two and would otherwise clobber optimistic query state\n // (e.g. an in-progress filter condition) on every navigation.\n const toNavOptions = useCallback((path: string) => {\n const relativePath = normalizeNavigationTarget({\n path,\n pathname: window.location.pathname,\n search: window.location.search,\n })\n\n const queryIndex = relativePath.indexOf('?')\n\n if (queryIndex === -1) {\n return { to: relativePath }\n }\n\n const searchObject = qs.parse(relativePath.slice(queryIndex + 1), {\n depth: 10,\n ignoreQueryPrefix: true,\n })\n\n // Function form replaces the search entirely (no merge with current search).\n return { search: () => searchObject, to: relativePath.slice(0, queryIndex) }\n }, [])\n\n const back = useCallback(() => router.history.back(), [router])\n\n const push = useCallback(\n (path: string, options?: { scroll?: boolean }) => {\n void router.navigate({ ...toNavOptions(path), resetScroll: options?.scroll })\n },\n [router, toNavOptions],\n )\n\n const refresh = useCallback(() => {\n void router.invalidate()\n }, [router])\n\n const replace = useCallback(\n (path: string, options?: { scroll?: boolean }) => {\n void router.navigate({ ...toNavOptions(path), replace: true, resetScroll: options?.scroll })\n },\n [router, toNavOptions],\n )\n\n // Mirror Next.js' router behavior to allow syncing client state to the URL without triggering a second server load.\n // TanStack's browser history monkeypatches `window.history.replaceState` and notifies `router.load` on every call.\n // Use `_ignoreSubscribers` to suppresses that notification — the same flag TanStack uses internally when flushing history.\n const replaceState = useCallback(\n (url: string) => {\n const { history } = router\n history._ignoreSubscribers = true\n\n try {\n window.history.replaceState(null, '', url)\n } finally {\n history._ignoreSubscribers = false\n }\n },\n [router],\n )\n\n const adaptedRouter = useMemo(\n () => ({ back, push, refresh, replace, replaceState }),\n [back, push, refresh, replace, replaceState],\n )\n\n // `location.searchStr` is the serialized query string; `location.search` is\n // the parsed object (a nested structure once the router uses `qs` for search\n // serialization), which `URLSearchParams` cannot consume. Build from the\n // string so Payload's `parseSearchParams` re-parses the bracket notation.\n const searchParams = useMemo(() => new URLSearchParams(location.searchStr), [location.searchStr])\n\n const value: RouterAdapterContextValue = useMemo(\n () => ({\n Link: TanStackLinkAdapter,\n params: adaptedParams,\n pathname: location.pathname,\n router: adaptedRouter,\n searchParams,\n }),\n [adaptedParams, location.pathname, adaptedRouter, searchParams],\n )\n\n return <RouterAdapterContext value={value}>{children}</RouterAdapterContext>\n}\n"],"names":["RouterAdapterContext","useRouteTransition","createLink","useLocation","useParams","useRouter","useRouterState","qs","React","useCallback","useEffect","useMemo","TanStackLink","Link","ariaCurrent","children","props","a","aria-current","normalizeNavigationTarget","path","pathname","search","startsWith","url","URL","hash","TanStackLinkAdapter","href","prefetch","ref","replace","scroll","rest","preload","resetScroll","to","TanStackRouterAdapter","router","location","params","strict","isRouterLoading","select","state","isLoading","holdRouteTransition","releaseRouteTransition","adaptedParams","adapted","_splat","segments","split","filter","Boolean","toNavOptions","relativePath","window","queryIndex","indexOf","searchObject","parse","slice","depth","ignoreQueryPrefix","back","history","push","options","navigate","refresh","invalidate","replaceState","_ignoreSubscribers","adaptedRouter","searchParams","URLSearchParams","searchStr","value"],"mappings":"AAAA;;AAKA,SAASA,oBAAoB,EAAEC,kBAAkB,QAAQ,iBAAgB;AACzE,SACEC,UAAU,EACVC,WAAW,EACXC,SAAS,EACTC,SAAS,EACTC,cAAc,QACT,yBAAwB;AAC/B,YAAYC,QAAQ,SAAQ;AAC5B,OAAOC,SAASC,WAAW,EAAEC,SAAS,EAAEC,OAAO,QAAQ,QAAO;AAM9D,MAAMC,eAAeV,WAAW,SAASW,KAAK,EAAEC,WAAW,EAAEC,QAAQ,EAAE,GAAGC,OAAkB;IAC1F,qBACE,KAACC;QAAG,GAAGD,KAAK;QAAEE,gBAAcJ,eAAeE,KAAK,CAAC,eAAe;kBAC7DD;;AAGP;AAEA,MAAMI,4BAA4B,CAAC,EACjCC,IAAI,EACJC,QAAQ,EACRC,MAAM,EAKP;IACC,IAAIF,KAAKG,UAAU,CAAC,SAAS;QAC3B,MAAMC,MAAM,IAAIC,IAAIL;QACpB,OAAO,GAAGI,IAAIH,QAAQ,GAAGG,IAAIF,MAAM,GAAGE,IAAIE,IAAI,EAAE;IAClD;IAEA,IAAIN,KAAKG,UAAU,CAAC,MAAM;QACxB,OAAO,GAAGF,WAAWD,MAAM;IAC7B;IAEA,IAAIA,KAAKG,UAAU,CAAC,MAAM;QACxB,OAAO,GAAGF,WAAWC,SAASF,MAAM;IACtC;IAEA,OAAOA;AACT;AAEA,MAAMO,sBAAkD,CAAC,EACvDZ,QAAQ,EACRa,IAAI,EACJC,QAAQ,EACRC,GAAG,EACHC,OAAO,EACPC,MAAM,EACN,GAAGC,MACJ;IACC,qBACE,KAACrB;QACCE,aAAamB,IAAI,CAAC,eAAe;QACjCC,SAASL,aAAa,QAAQ,QAAQ;QACtCC,KAAKA;QACLC,SAASA;QACTI,aAAaH;QACbI,IAAIR;QACH,GAAGK,IAAI;kBAEPlB;;AAGP;AAEA,OAAO,MAAMsB,wBAAgD,CAAC,EAAEtB,QAAQ,EAAE;IACxE,MAAMuB,SAASjC;IACf,MAAMkC,WAAWpC;IACjB,MAAMqC,SAASpC,UAAU;QAAEqC,QAAQ;IAAM;IACzC,MAAMC,kBAAkBpC,eAAe;QAAEqC,QAAQ,CAACC,QAAUA,MAAMC,SAAS;IAAC;IAC5E,MAAM,EAAEC,mBAAmB,EAAE,GAAG7C;IAEhCS,UAAU;QACR,IAAI,CAACgC,iBAAiB;YACpB;QACF;QAEA,MAAMK,yBAAyBD;QAE/B,OAAO,IAAMC;IACf,GAAG;QAACD;QAAqBJ;KAAgB;IAEzC,MAAMM,gBAAgBrC,QAAQ;QAC5B,MAAMsC,UAA6C;YAAE,GAAGT,MAAM;QAAC;QAE/D,IAAI,YAAYA,UAAU,OAAOA,OAAOU,MAAM,KAAK,UAAU;YAC3DD,QAAQE,QAAQ,GAAGX,OAAOU,MAAM,CAACE,KAAK,CAAC,KAAKC,MAAM,CAACC;QACrD;QAEA,OAAOL;IACT,GAAG;QAACT;KAAO;IAEX,yEAAyE;IACzE,8EAA8E;IAC9E,8EAA8E;IAC9E,4DAA4D;IAC5D,4EAA4E;IAC5E,6EAA6E;IAC7E,4EAA4E;IAC5E,8DAA8D;IAC9D,MAAMe,eAAe9C,YAAY,CAACW;QAChC,MAAMoC,eAAerC,0BAA0B;YAC7CC;YACAC,UAAUoC,OAAOlB,QAAQ,CAAClB,QAAQ;YAClCC,QAAQmC,OAAOlB,QAAQ,CAACjB,MAAM;QAChC;QAEA,MAAMoC,aAAaF,aAAaG,OAAO,CAAC;QAExC,IAAID,eAAe,CAAC,GAAG;YACrB,OAAO;gBAAEtB,IAAIoB;YAAa;QAC5B;QAEA,MAAMI,eAAerD,GAAGsD,KAAK,CAACL,aAAaM,KAAK,CAACJ,aAAa,IAAI;YAChEK,OAAO;YACPC,mBAAmB;QACrB;QAEA,6EAA6E;QAC7E,OAAO;YAAE1C,QAAQ,IAAMsC;YAAcxB,IAAIoB,aAAaM,KAAK,CAAC,GAAGJ;QAAY;IAC7E,GAAG,EAAE;IAEL,MAAMO,OAAOxD,YAAY,IAAM6B,OAAO4B,OAAO,CAACD,IAAI,IAAI;QAAC3B;KAAO;IAE9D,MAAM6B,OAAO1D,YACX,CAACW,MAAcgD;QACb,KAAK9B,OAAO+B,QAAQ,CAAC;YAAE,GAAGd,aAAanC,KAAK;YAAEe,aAAaiC,SAASpC;QAAO;IAC7E,GACA;QAACM;QAAQiB;KAAa;IAGxB,MAAMe,UAAU7D,YAAY;QAC1B,KAAK6B,OAAOiC,UAAU;IACxB,GAAG;QAACjC;KAAO;IAEX,MAAMP,UAAUtB,YACd,CAACW,MAAcgD;QACb,KAAK9B,OAAO+B,QAAQ,CAAC;YAAE,GAAGd,aAAanC,KAAK;YAAEW,SAAS;YAAMI,aAAaiC,SAASpC;QAAO;IAC5F,GACA;QAACM;QAAQiB;KAAa;IAGxB,oHAAoH;IACpH,mHAAmH;IACnH,2HAA2H;IAC3H,MAAMiB,eAAe/D,YACnB,CAACe;QACC,MAAM,EAAE0C,OAAO,EAAE,GAAG5B;QACpB4B,QAAQO,kBAAkB,GAAG;QAE7B,IAAI;YACFhB,OAAOS,OAAO,CAACM,YAAY,CAAC,MAAM,IAAIhD;QACxC,SAAU;YACR0C,QAAQO,kBAAkB,GAAG;QAC/B;IACF,GACA;QAACnC;KAAO;IAGV,MAAMoC,gBAAgB/D,QACpB,IAAO,CAAA;YAAEsD;YAAME;YAAMG;YAASvC;YAASyC;QAAa,CAAA,GACpD;QAACP;QAAME;QAAMG;QAASvC;QAASyC;KAAa;IAG9C,4EAA4E;IAC5E,6EAA6E;IAC7E,yEAAyE;IACzE,0EAA0E;IAC1E,MAAMG,eAAehE,QAAQ,IAAM,IAAIiE,gBAAgBrC,SAASsC,SAAS,GAAG;QAACtC,SAASsC,SAAS;KAAC;IAEhG,MAAMC,QAAmCnE,QACvC,IAAO,CAAA;YACLE,MAAMc;YACNa,QAAQQ;YACR3B,UAAUkB,SAASlB,QAAQ;YAC3BiB,QAAQoC;YACRC;QACF,CAAA,GACA;QAAC3B;QAAeT,SAASlB,QAAQ;QAAEqD;QAAeC;KAAa;IAGjE,qBAAO,KAAC3E;QAAqB8E,OAAOA;kBAAQ/D;;AAC9C,EAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ServerAdapter } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* Server-side adapter for TanStack Start.
|
|
4
|
+
* Uses `@tanstack/react-start/server` utilities for request/response handling.
|
|
5
|
+
*/
|
|
6
|
+
export declare const tanstackServerAdapter: ServerAdapter;
|
|
7
|
+
/**
|
|
8
|
+
* Navigation requested during an admin page render, recorded by
|
|
9
|
+
* `createPageRenderServerAdapter`. The admin-page server function reads this
|
|
10
|
+
* after `renderServerComponent` resolves.
|
|
11
|
+
*/
|
|
12
|
+
export type PageNavIntent = {
|
|
13
|
+
type?: 'notFound' | 'redirect';
|
|
14
|
+
url?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Builds the `ServerAdapter` for the admin page render (`renderRoot`). Like
|
|
18
|
+
* `tanstackServerAdapter` for cookies/headers, but navigation **records the
|
|
19
|
+
* intent on `nav` and throws** the framework-agnostic string error-contract
|
|
20
|
+
* (`Error('not-found')` / `Error('redirect:<url>')`) instead of TanStack's
|
|
21
|
+
* native `notFound`/`redirect`.
|
|
22
|
+
*
|
|
23
|
+
* `renderRoot` navigates via `req.server.*` in two places:
|
|
24
|
+
* - during orchestration (e.g. the unauthenticated login redirect) — the throw
|
|
25
|
+
* propagates out of `renderRoot` and is caught by the loader's try/catch;
|
|
26
|
+
* - deep inside streamed view components (e.g. `DocumentView` access checks,
|
|
27
|
+
* `LoginView` already-authenticated) — here the throw happens during
|
|
28
|
+
* `renderServerComponent` and is swallowed into the RSC stream, so it never
|
|
29
|
+
* rejects the render promise.
|
|
30
|
+
*
|
|
31
|
+
* Recording the intent on `nav` lets the loader honor the second case by
|
|
32
|
+
* inspecting `nav` after the render resolves, then re-throwing native TanStack
|
|
33
|
+
* nav at the loader boundary.
|
|
34
|
+
*/
|
|
35
|
+
export declare const createPageRenderServerAdapter: (nav: PageNavIntent) => ServerAdapter;
|
|
36
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/adapters/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA8B,aAAa,EAAE,MAAM,SAAS,CAAA;AAsDxE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,aA8CnC,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CAAA;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,6BAA6B,QAAS,aAAa,KAAG,aAgBjE,CAAA"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { notFound, redirect } from '@tanstack/react-router';
|
|
2
|
+
import { getRequest, getResponseHeaders } from '@tanstack/react-start/server';
|
|
3
|
+
import { parseCookies } from 'payload';
|
|
4
|
+
function buildCookieStore(headers) {
|
|
5
|
+
const cookies = parseCookies(headers);
|
|
6
|
+
return {
|
|
7
|
+
get: (name)=>{
|
|
8
|
+
const value = cookies.get(name);
|
|
9
|
+
return value !== undefined ? {
|
|
10
|
+
name,
|
|
11
|
+
value
|
|
12
|
+
} : undefined;
|
|
13
|
+
},
|
|
14
|
+
getAll: ()=>{
|
|
15
|
+
return Array.from(cookies.entries()).map(([name, value])=>({
|
|
16
|
+
name,
|
|
17
|
+
value
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function serializeCookie(name, value, options) {
|
|
23
|
+
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
|
|
24
|
+
if (options?.path) {
|
|
25
|
+
cookie += `; Path=${options.path}`;
|
|
26
|
+
}
|
|
27
|
+
if (options?.domain) {
|
|
28
|
+
cookie += `; Domain=${options.domain}`;
|
|
29
|
+
}
|
|
30
|
+
if (options?.maxAge !== undefined) {
|
|
31
|
+
cookie += `; Max-Age=${options.maxAge}`;
|
|
32
|
+
}
|
|
33
|
+
if (options?.expires) {
|
|
34
|
+
cookie += `; Expires=${options.expires.toUTCString()}`;
|
|
35
|
+
}
|
|
36
|
+
if (options?.httpOnly) {
|
|
37
|
+
cookie += '; HttpOnly';
|
|
38
|
+
}
|
|
39
|
+
if (options?.secure) {
|
|
40
|
+
cookie += '; Secure';
|
|
41
|
+
}
|
|
42
|
+
if (options?.sameSite) {
|
|
43
|
+
cookie += `; SameSite=${options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1)}`;
|
|
44
|
+
}
|
|
45
|
+
return cookie;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Server-side adapter for TanStack Start.
|
|
49
|
+
* Uses `@tanstack/react-start/server` utilities for request/response handling.
|
|
50
|
+
*/ export const tanstackServerAdapter = {
|
|
51
|
+
getCookies: ()=>{
|
|
52
|
+
const request = getRequest();
|
|
53
|
+
const headers = new Headers(request.headers);
|
|
54
|
+
return buildCookieStore(headers);
|
|
55
|
+
},
|
|
56
|
+
getHeaders: ()=>{
|
|
57
|
+
const request = getRequest();
|
|
58
|
+
return new Headers(request.headers);
|
|
59
|
+
},
|
|
60
|
+
notFound: ()=>{
|
|
61
|
+
// TanStack Router requires throwing the notFound() result directly
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
63
|
+
throw notFound();
|
|
64
|
+
},
|
|
65
|
+
redirect: (path)=>{
|
|
66
|
+
// TanStack Router requires throwing the redirect() result directly
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
68
|
+
throw redirect({
|
|
69
|
+
to: path
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
permanentRedirect: (path)=>{
|
|
73
|
+
// TanStack Router does not have a separate permanent redirect primitive;
|
|
74
|
+
// fall back to a regular redirect so existing behavior is preserved.
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
76
|
+
throw redirect({
|
|
77
|
+
to: path
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
forbidden: ()=>{
|
|
81
|
+
// TanStack Router does not have a dedicated forbidden() helper; surface
|
|
82
|
+
// a generic error so the request boundary still terminates the request.
|
|
83
|
+
throw new Error('Forbidden');
|
|
84
|
+
},
|
|
85
|
+
unauthorized: ()=>{
|
|
86
|
+
// TanStack Router does not have a dedicated unauthorized() helper; surface
|
|
87
|
+
// a generic error so the request boundary still terminates the request.
|
|
88
|
+
throw new Error('Unauthorized');
|
|
89
|
+
},
|
|
90
|
+
setCookie: (name, value, options)=>{
|
|
91
|
+
getResponseHeaders().append('Set-Cookie', serializeCookie(name, value, options));
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Builds the `ServerAdapter` for the admin page render (`renderRoot`). Like
|
|
96
|
+
* `tanstackServerAdapter` for cookies/headers, but navigation **records the
|
|
97
|
+
* intent on `nav` and throws** the framework-agnostic string error-contract
|
|
98
|
+
* (`Error('not-found')` / `Error('redirect:<url>')`) instead of TanStack's
|
|
99
|
+
* native `notFound`/`redirect`.
|
|
100
|
+
*
|
|
101
|
+
* `renderRoot` navigates via `req.server.*` in two places:
|
|
102
|
+
* - during orchestration (e.g. the unauthenticated login redirect) — the throw
|
|
103
|
+
* propagates out of `renderRoot` and is caught by the loader's try/catch;
|
|
104
|
+
* - deep inside streamed view components (e.g. `DocumentView` access checks,
|
|
105
|
+
* `LoginView` already-authenticated) — here the throw happens during
|
|
106
|
+
* `renderServerComponent` and is swallowed into the RSC stream, so it never
|
|
107
|
+
* rejects the render promise.
|
|
108
|
+
*
|
|
109
|
+
* Recording the intent on `nav` lets the loader honor the second case by
|
|
110
|
+
* inspecting `nav` after the render resolves, then re-throwing native TanStack
|
|
111
|
+
* nav at the loader boundary.
|
|
112
|
+
*/ export const createPageRenderServerAdapter = (nav)=>({
|
|
113
|
+
...tanstackServerAdapter,
|
|
114
|
+
notFound: ()=>{
|
|
115
|
+
nav.type = 'notFound';
|
|
116
|
+
throw new Error('not-found');
|
|
117
|
+
},
|
|
118
|
+
permanentRedirect: (path)=>{
|
|
119
|
+
nav.type = 'redirect';
|
|
120
|
+
nav.url = path;
|
|
121
|
+
throw new Error(`redirect:${path}`);
|
|
122
|
+
},
|
|
123
|
+
redirect: (path)=>{
|
|
124
|
+
nav.type = 'redirect';
|
|
125
|
+
nav.url = path;
|
|
126
|
+
throw new Error(`redirect:${path}`);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/server.ts"],"sourcesContent":["import type { CookieOptions, CookieStore, ServerAdapter } from 'payload'\n\nimport { notFound, redirect } from '@tanstack/react-router'\nimport { getRequest, getResponseHeaders } from '@tanstack/react-start/server'\nimport { parseCookies } from 'payload'\n\nfunction buildCookieStore(headers: Headers): CookieStore {\n const cookies = parseCookies(headers)\n\n return {\n get: (name: string) => {\n const value = cookies.get(name)\n return value !== undefined ? { name, value } : undefined\n },\n getAll: () => {\n return Array.from(cookies.entries()).map(([name, value]) => ({ name, value }))\n },\n }\n}\n\nfunction serializeCookie(name: string, value: string, options?: CookieOptions): string {\n let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`\n\n if (options?.path) {\n cookie += `; Path=${options.path}`\n }\n\n if (options?.domain) {\n cookie += `; Domain=${options.domain}`\n }\n\n if (options?.maxAge !== undefined) {\n cookie += `; Max-Age=${options.maxAge}`\n }\n\n if (options?.expires) {\n cookie += `; Expires=${options.expires.toUTCString()}`\n }\n\n if (options?.httpOnly) {\n cookie += '; HttpOnly'\n }\n\n if (options?.secure) {\n cookie += '; Secure'\n }\n\n if (options?.sameSite) {\n cookie += `; SameSite=${options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1)}`\n }\n\n return cookie\n}\n\n/**\n * Server-side adapter for TanStack Start.\n * Uses `@tanstack/react-start/server` utilities for request/response handling.\n */\nexport const tanstackServerAdapter: ServerAdapter = {\n getCookies: () => {\n const request = getRequest()\n const headers = new Headers(request.headers)\n return buildCookieStore(headers)\n },\n\n getHeaders: () => {\n const request = getRequest()\n return new Headers(request.headers)\n },\n\n notFound: () => {\n // TanStack Router requires throwing the notFound() result directly\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw notFound()\n },\n\n redirect: (path: string) => {\n // TanStack Router requires throwing the redirect() result directly\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw redirect({ to: path })\n },\n\n permanentRedirect: (path: string) => {\n // TanStack Router does not have a separate permanent redirect primitive;\n // fall back to a regular redirect so existing behavior is preserved.\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw redirect({ to: path })\n },\n\n forbidden: () => {\n // TanStack Router does not have a dedicated forbidden() helper; surface\n // a generic error so the request boundary still terminates the request.\n throw new Error('Forbidden')\n },\n\n unauthorized: () => {\n // TanStack Router does not have a dedicated unauthorized() helper; surface\n // a generic error so the request boundary still terminates the request.\n throw new Error('Unauthorized')\n },\n\n setCookie: (name: string, value: string, options?: CookieOptions) => {\n getResponseHeaders().append('Set-Cookie', serializeCookie(name, value, options))\n },\n}\n\n/**\n * Navigation requested during an admin page render, recorded by\n * `createPageRenderServerAdapter`. The admin-page server function reads this\n * after `renderServerComponent` resolves.\n */\nexport type PageNavIntent = {\n type?: 'notFound' | 'redirect'\n url?: string\n}\n\n/**\n * Builds the `ServerAdapter` for the admin page render (`renderRoot`). Like\n * `tanstackServerAdapter` for cookies/headers, but navigation **records the\n * intent on `nav` and throws** the framework-agnostic string error-contract\n * (`Error('not-found')` / `Error('redirect:<url>')`) instead of TanStack's\n * native `notFound`/`redirect`.\n *\n * `renderRoot` navigates via `req.server.*` in two places:\n * - during orchestration (e.g. the unauthenticated login redirect) — the throw\n * propagates out of `renderRoot` and is caught by the loader's try/catch;\n * - deep inside streamed view components (e.g. `DocumentView` access checks,\n * `LoginView` already-authenticated) — here the throw happens during\n * `renderServerComponent` and is swallowed into the RSC stream, so it never\n * rejects the render promise.\n *\n * Recording the intent on `nav` lets the loader honor the second case by\n * inspecting `nav` after the render resolves, then re-throwing native TanStack\n * nav at the loader boundary.\n */\nexport const createPageRenderServerAdapter = (nav: PageNavIntent): ServerAdapter => ({\n ...tanstackServerAdapter,\n notFound: () => {\n nav.type = 'notFound'\n throw new Error('not-found')\n },\n permanentRedirect: (path: string) => {\n nav.type = 'redirect'\n nav.url = path\n throw new Error(`redirect:${path}`)\n },\n redirect: (path: string) => {\n nav.type = 'redirect'\n nav.url = path\n throw new Error(`redirect:${path}`)\n },\n})\n"],"names":["notFound","redirect","getRequest","getResponseHeaders","parseCookies","buildCookieStore","headers","cookies","get","name","value","undefined","getAll","Array","from","entries","map","serializeCookie","options","cookie","encodeURIComponent","path","domain","maxAge","expires","toUTCString","httpOnly","secure","sameSite","charAt","toUpperCase","slice","tanstackServerAdapter","getCookies","request","Headers","getHeaders","to","permanentRedirect","forbidden","Error","unauthorized","setCookie","append","createPageRenderServerAdapter","nav","type","url"],"mappings":"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"}
|