@payloadcms/tanstack-start 4.0.0-internal.bc5cf8c → 4.0.0-internal.c7d5de4
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 +1 -1
- package/dist/elements/RouterAdapter/index.d.ts.map +1 -1
- package/dist/elements/RouterAdapter/index.js +18 -2
- package/dist/elements/RouterAdapter/index.js.map +1 -1
- package/dist/exports/client.d.ts +1 -1
- package/dist/exports/client.d.ts.map +1 -1
- package/dist/exports/client.js +1 -1
- package/dist/exports/client.js.map +1 -1
- package/dist/exports/server.d.ts +3 -2
- package/dist/exports/server.d.ts.map +1 -1
- package/dist/exports/server.js +3 -2
- package/dist/exports/server.js.map +1 -1
- package/dist/exports/vite.d.ts +2 -2
- package/dist/exports/vite.d.ts.map +1 -1
- package/dist/exports/vite.js +1 -1
- package/dist/exports/vite.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/layouts/Root/getLayoutData.d.ts.map +1 -1
- package/dist/layouts/Root/getLayoutData.js +1 -1
- package/dist/layouts/Root/getLayoutData.js.map +1 -1
- package/dist/layouts/Root/index.d.ts +2 -2
- package/dist/layouts/Root/index.d.ts.map +1 -1
- package/dist/layouts/Root/index.js +4 -2
- package/dist/layouts/Root/index.js.map +1 -1
- package/dist/layouts/Root/withPayloadRoot.d.ts +7 -31
- package/dist/layouts/Root/withPayloadRoot.d.ts.map +1 -1
- package/dist/layouts/Root/withPayloadRoot.js +33 -34
- package/dist/layouts/Root/withPayloadRoot.js.map +1 -1
- package/dist/node/cssLoader.d.mts +24 -2
- package/dist/node/cssLoader.d.mts.map +1 -1
- package/dist/routes/adminRoutes.d.ts +13 -7
- package/dist/routes/adminRoutes.d.ts.map +1 -1
- package/dist/routes/adminRoutes.js +31 -20
- package/dist/routes/adminRoutes.js.map +1 -1
- package/dist/routes/apiRoute.d.ts +5 -0
- package/dist/routes/apiRoute.d.ts.map +1 -1
- package/dist/routes/apiRoute.js +6 -1
- package/dist/routes/apiRoute.js.map +1 -1
- package/dist/routes/layoutRoute.d.ts +4 -1
- package/dist/routes/layoutRoute.d.ts.map +1 -1
- package/dist/routes/layoutRoute.js +7 -1
- package/dist/routes/layoutRoute.js.map +1 -1
- package/dist/utilities/graphqlHandler.server.d.ts.map +1 -1
- package/dist/utilities/{handleAPIRoute.d.ts → handleAPIRoute.server.d.ts} +1 -1
- package/dist/utilities/handleAPIRoute.server.d.ts.map +1 -0
- package/dist/utilities/{handleAPIRoute.js → handleAPIRoute.server.js} +1 -1
- package/dist/utilities/handleAPIRoute.server.js.map +1 -0
- package/dist/utilities/loadAdminPage.d.ts.map +1 -1
- package/dist/utilities/loadAdminPage.js +24 -2
- package/dist/utilities/loadAdminPage.js.map +1 -1
- package/dist/utilities/meta.d.ts +1 -0
- package/dist/utilities/meta.d.ts.map +1 -1
- package/dist/utilities/meta.js +2 -1
- package/dist/utilities/meta.js.map +1 -1
- 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/serverAdapter.server.d.ts.map +1 -1
- package/dist/vite/config/external.d.ts +26 -0
- package/dist/vite/config/external.d.ts.map +1 -0
- package/dist/vite/config/external.js +72 -0
- package/dist/vite/config/external.js.map +1 -0
- package/dist/vite/config/optimizeDeps.d.ts +25 -0
- package/dist/vite/config/optimizeDeps.d.ts.map +1 -0
- package/dist/vite/{constants.js → config/optimizeDeps.js} +9 -63
- package/dist/vite/config/optimizeDeps.js.map +1 -0
- package/dist/vite/config/scss.d.ts +14 -0
- package/dist/vite/config/scss.d.ts.map +1 -0
- package/dist/vite/config/scss.js +28 -0
- package/dist/vite/config/scss.js.map +1 -0
- package/dist/vite/index.d.ts +99 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +258 -0
- package/dist/vite/index.js.map +1 -0
- package/dist/vite/workarounds/clientModuleResolution.d.ts.map +1 -0
- package/dist/vite/workarounds/clientModuleResolution.js.map +1 -0
- package/dist/vite/workarounds/devTransforms.d.ts.map +1 -0
- package/dist/vite/{plugins → workarounds}/devTransforms.js +5 -0
- package/dist/vite/workarounds/devTransforms.js.map +1 -0
- package/dist/vite/workarounds/reactDomServerInRsc.d.ts.map +1 -0
- package/dist/vite/workarounds/reactDomServerInRsc.js.map +1 -0
- package/dist/vite/workarounds/stripDistStyleImports.d.ts.map +1 -0
- package/dist/vite/workarounds/stripDistStyleImports.js.map +1 -0
- package/dist/vite/workarounds/stubPrettierInClient.d.ts +13 -0
- package/dist/vite/workarounds/stubPrettierInClient.d.ts.map +1 -0
- package/dist/vite/workarounds/stubPrettierInClient.js +32 -0
- package/dist/vite/workarounds/stubPrettierInClient.js.map +1 -0
- package/dist/vite/workarounds/wrapCjsForClient.d.ts.map +1 -0
- package/dist/vite/workarounds/wrapCjsForClient.js.map +1 -0
- package/package.json +10 -5
- package/dist/@types/assets.d.js +0 -2
- package/dist/@types/assets.d.js.map +0 -1
- package/dist/utilities/getRequestTheme.d.ts +0 -10
- package/dist/utilities/getRequestTheme.d.ts.map +0 -1
- package/dist/utilities/getRequestTheme.js +0 -25
- package/dist/utilities/getRequestTheme.js.map +0 -1
- package/dist/utilities/handleAPIRoute.d.ts.map +0 -1
- package/dist/utilities/handleAPIRoute.js.map +0 -1
- package/dist/vite/constants.d.ts +0 -42
- package/dist/vite/constants.d.ts.map +0 -1
- package/dist/vite/constants.js.map +0 -1
- package/dist/vite/plugin.d.ts +0 -38
- package/dist/vite/plugin.d.ts.map +0 -1
- package/dist/vite/plugin.js +0 -179
- package/dist/vite/plugin.js.map +0 -1
- package/dist/vite/plugins/clientModuleResolution.d.ts.map +0 -1
- package/dist/vite/plugins/clientModuleResolution.js.map +0 -1
- package/dist/vite/plugins/devTransforms.d.ts.map +0 -1
- package/dist/vite/plugins/devTransforms.js.map +0 -1
- package/dist/vite/plugins/reactDomServerInRsc.d.ts.map +0 -1
- package/dist/vite/plugins/reactDomServerInRsc.js.map +0 -1
- package/dist/vite/plugins/stripDistStyleImports.d.ts.map +0 -1
- package/dist/vite/plugins/stripDistStyleImports.js.map +0 -1
- package/dist/vite/plugins/wrapCjsForClient.d.ts.map +0 -1
- package/dist/vite/plugins/wrapCjsForClient.js.map +0 -1
- /package/dist/vite/{plugins → workarounds}/clientModuleResolution.d.ts +0 -0
- /package/dist/vite/{plugins → workarounds}/clientModuleResolution.js +0 -0
- /package/dist/vite/{plugins → workarounds}/devTransforms.d.ts +0 -0
- /package/dist/vite/{plugins → workarounds}/reactDomServerInRsc.d.ts +0 -0
- /package/dist/vite/{plugins → workarounds}/reactDomServerInRsc.js +0 -0
- /package/dist/vite/{plugins → workarounds}/stripDistStyleImports.d.ts +0 -0
- /package/dist/vite/{plugins → workarounds}/stripDistStyleImports.js +0 -0
- /package/dist/vite/{plugins → workarounds}/wrapCjsForClient.d.ts +0 -0
- /package/dist/vite/{plugins → workarounds}/wrapCjsForClient.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/layouts/Root/withPayloadRoot.tsx"],"sourcesContent":["'use client'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/layouts/Root/withPayloadRoot.tsx"],"sourcesContent":["'use client'\nimport type { Theme } from '@payloadcms/ui'\n\nimport { getLanguageDir } from '@payloadcms/ui/utilities/getLanguageDir'\nimport { HeadContent, Scripts, useRouterState } from '@tanstack/react-router'\nimport React from 'react'\n\ntype AdminHTMLProps = {\n 'data-theme'?: Theme\n dir: 'ltr' | 'rtl'\n lang?: string\n}\n\nexport type PayloadAdminShellProps = {\n readonly children: React.ReactNode\n}\n\n/**\n * The `<html>` document shell for Payload admin routes — the TanStack Start\n * equivalent of `@payloadcms/next`'s root layout `<html>`. Sets\n * `data-theme`/`lang`/`dir` on `<html>` from the server-computed layout data\n * (`getLayoutData`, exposed on the `/_payload` route loader), so the admin\n * panel renders themed with the correct text direction on the first paint with no\n * client bootstrap script — the same server-side path Next's `RootLayout`\n * uses, sharing `getRequestTheme`/`getLanguageDir` from `@payloadcms/ui`.\n */\nexport function PayloadAdminShell({ children }: PayloadAdminShellProps) {\n const htmlProps = useRouterState({\n select: (state): AdminHTMLProps => {\n for (const match of state.matches) {\n const data = match.loaderData as { languageCode?: string; theme?: Theme } | undefined\n\n if (data?.theme && data?.languageCode) {\n return {\n 'data-theme': data.theme,\n dir: getLanguageDir({ languageCode: data.languageCode }),\n lang: data.languageCode,\n }\n }\n }\n\n // No layout data yet (fresh session before the loader resolves): default\n // to `ltr` so the `[dir='ltr']`-scoped admin layout rules (e.g. the\n // document sidebar divider) still match, matching Next's `ltr` default.\n return { dir: 'ltr' }\n },\n })\n\n return (\n // eslint-disable-next-line jsx-a11y/html-has-lang -- `lang` is set from server-computed layout data when available\n <html {...htmlProps} suppressHydrationWarning>\n <head>\n <style>{`@layer payload-default, payload;`}</style>\n <HeadContent />\n </head>\n <body>\n {children}\n <Scripts />\n </body>\n </html>\n )\n}\n\nexport type WithPayloadRootOptions = {\n /**\n * Path prefix that mounts the Payload admin panel (`config.routes.admin`).\n * Routes under it render the Payload admin document shell; everything else\n * renders your own shell. Defaults to `'/admin'`.\n */\n adminRoute?: string\n}\n\n/**\n * Wraps your application's root document shell so Payload owns its own\n * `<html>` chrome on admin routes while your shell renders everywhere else.\n *\n * Attach the result to the root route's `shellComponent`; it is the single\n * integration touch point — no root loader and no manual data threading:\n *\n * ```tsx\n * export const Route = createRootRoute({\n * shellComponent: withPayloadRoot(MarketingRoot),\n * })\n * ```\n */\nexport function withPayloadRoot(\n RootShell: React.ComponentType<{ children: React.ReactNode }>,\n options: WithPayloadRootOptions = {},\n) {\n const { adminRoute = '/admin' } = options\n\n return function PayloadRootShell({ children }: { children: React.ReactNode }) {\n const isAdminRoute = useRouterState({\n select: (s) => {\n const { pathname } = s.location\n return pathname === adminRoute || pathname.startsWith(`${adminRoute}/`)\n },\n })\n\n if (isAdminRoute) {\n return <PayloadAdminShell>{children}</PayloadAdminShell>\n }\n\n return <RootShell>{children}</RootShell>\n }\n}\n"],"names":["getLanguageDir","HeadContent","Scripts","useRouterState","React","PayloadAdminShell","children","htmlProps","select","state","match","matches","data","loaderData","theme","languageCode","dir","lang","html","suppressHydrationWarning","head","style","body","withPayloadRoot","RootShell","options","adminRoute","PayloadRootShell","isAdminRoute","s","pathname","location","startsWith"],"mappings":"AAAA;;AAGA,SAASA,cAAc,QAAQ,0CAAyC;AACxE,SAASC,WAAW,EAAEC,OAAO,EAAEC,cAAc,QAAQ,yBAAwB;AAC7E,OAAOC,WAAW,QAAO;AAYzB;;;;;;;;CAQC,GACD,OAAO,SAASC,kBAAkB,EAAEC,QAAQ,EAA0B;IACpE,MAAMC,YAAYJ,eAAe;QAC/BK,QAAQ,CAACC;YACP,KAAK,MAAMC,SAASD,MAAME,OAAO,CAAE;gBACjC,MAAMC,OAAOF,MAAMG,UAAU;gBAE7B,IAAID,MAAME,SAASF,MAAMG,cAAc;oBACrC,OAAO;wBACL,cAAcH,KAAKE,KAAK;wBACxBE,KAAKhB,eAAe;4BAAEe,cAAcH,KAAKG,YAAY;wBAAC;wBACtDE,MAAML,KAAKG,YAAY;oBACzB;gBACF;YACF;YAEA,yEAAyE;YACzE,oEAAoE;YACpE,wEAAwE;YACxE,OAAO;gBAAEC,KAAK;YAAM;QACtB;IACF;IAEA,OACE,mHAAmH;kBACnH,MAACE;QAAM,GAAGX,SAAS;QAAEY,wBAAwB;;0BAC3C,MAACC;;kCACC,KAACC;kCAAO,CAAC,gCAAgC,CAAC;;kCAC1C,KAACpB;;;0BAEH,MAACqB;;oBACEhB;kCACD,KAACJ;;;;;AAIT;AAWA;;;;;;;;;;;;CAYC,GACD,OAAO,SAASqB,gBACdC,SAA6D,EAC7DC,UAAkC,CAAC,CAAC;IAEpC,MAAM,EAAEC,aAAa,QAAQ,EAAE,GAAGD;IAElC,OAAO,SAASE,iBAAiB,EAAErB,QAAQ,EAAiC;QAC1E,MAAMsB,eAAezB,eAAe;YAClCK,QAAQ,CAACqB;gBACP,MAAM,EAAEC,QAAQ,EAAE,GAAGD,EAAEE,QAAQ;gBAC/B,OAAOD,aAAaJ,cAAcI,SAASE,UAAU,CAAC,GAAGN,WAAW,CAAC,CAAC;YACxE;QACF;QAEA,IAAIE,cAAc;YAChB,qBAAO,KAACvB;0BAAmBC;;QAC7B;QAEA,qBAAO,KAACkB;sBAAWlB;;IACrB;AACF"}
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Node ESM loader hook that swallows static asset imports (CSS/SCSS/LESS,
|
|
3
|
+
* images, fonts, audio, video, etc.) encountered while running the dev
|
|
4
|
+
* server.
|
|
5
|
+
*
|
|
6
|
+
* Vite is supposed to intercept asset imports during SSR/RSC by treating
|
|
7
|
+
* the package as `noExternal`, but that resolution path silently breaks down
|
|
8
|
+
* when a dependency is installed from a packed tarball under
|
|
9
|
+
* `node_modules/.pnpm/<pkg>@file+packed+...`. In that scenario Vite hands the
|
|
10
|
+
* import to Node's native ESM loader, which has no opinion about `.css` /
|
|
11
|
+
* `.svg` / `.png` files and throws `ERR_UNKNOWN_FILE_EXTENSION`. That single
|
|
12
|
+
* throw cascades into every admin route showing "Something went wrong!"
|
|
13
|
+
* because the SSR tree dies inside `<MatchInnerImpl>`.
|
|
14
|
+
*
|
|
15
|
+
* Registering this loader (with `--import` or `register()`) makes Node treat
|
|
16
|
+
* any asset specifier as an empty ES module, which is exactly what we
|
|
17
|
+
* already do client-side for SSR.
|
|
18
|
+
*
|
|
19
|
+
* The loader is intentionally minimal and side-effect free; it only kicks in
|
|
20
|
+
* for asset specifiers and falls through to the default loader for
|
|
21
|
+
* everything else.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolve(specifier: any, context: any, nextResolve: any): Promise<any>;
|
|
24
|
+
export declare function load(url: any, context: any, nextLoad: any): Promise<any>;
|
|
3
25
|
//# sourceMappingURL=cssLoader.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cssLoader.d.mts","sourceRoot":"","sources":["../../src/node/cssLoader.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cssLoader.d.mts","sourceRoot":"","sources":["../../src/node/cssLoader.mjs"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAiCH,wBAAsB,OAAO,CAAC,SAAS,KAAA,EAAE,OAAO,KAAA,EAAE,WAAW,KAAA,gBAS5D;AAED,wBAAsB,IAAI,CAAC,GAAG,KAAA,EAAE,OAAO,KAAA,EAAE,QAAQ,KAAA,gBAShD"}
|
|
@@ -49,10 +49,13 @@ export declare function payloadAdminSplatRoute({ load }: {
|
|
|
49
49
|
title: string;
|
|
50
50
|
})[];
|
|
51
51
|
};
|
|
52
|
-
loader:
|
|
53
|
-
location:
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
loader: {
|
|
53
|
+
handler: ({ location, params }: {
|
|
54
|
+
location: any;
|
|
55
|
+
params: any;
|
|
56
|
+
}) => Promise<any>;
|
|
57
|
+
staleReloadMode: string;
|
|
58
|
+
};
|
|
56
59
|
loaderDeps: ({ search }: {
|
|
57
60
|
search: Record<string, unknown>;
|
|
58
61
|
}) => {
|
|
@@ -91,9 +94,12 @@ export declare function payloadAdminIndexRoute({ load }: {
|
|
|
91
94
|
title: string;
|
|
92
95
|
})[];
|
|
93
96
|
};
|
|
94
|
-
loader:
|
|
95
|
-
location:
|
|
96
|
-
|
|
97
|
+
loader: {
|
|
98
|
+
handler: ({ location }: {
|
|
99
|
+
location: any;
|
|
100
|
+
}) => Promise<any>;
|
|
101
|
+
staleReloadMode: string;
|
|
102
|
+
};
|
|
97
103
|
loaderDeps: ({ search }: {
|
|
98
104
|
search: Record<string, unknown>;
|
|
99
105
|
}) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adminRoutes.d.ts","sourceRoot":"","sources":["../../src/routes/adminRoutes.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAIhD;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE;IAC7B,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAA;KAAE,CAAA;CACpE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;AAclB,iBAAS,SAAS,gCAQjB;AAED,iBAAS,aAAa,CAAC,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,SAAS,CAAA;KAAE,CAAA;CAAE,+BAMrF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE
|
|
1
|
+
{"version":3,"file":"adminRoutes.d.ts","sourceRoot":"","sources":["../../src/routes/adminRoutes.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAIhD;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE;IAC7B,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAA;KAAE,CAAA;CACpE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;AAclB,iBAAS,SAAS,gCAQjB;AAED,iBAAS,aAAa,CAAC,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,SAAS,CAAA;KAAE,CAAA;CAAE,+BAMrF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE;IAEhE,SAAS;IACT,IAAI,mBAAmB;QAAE,UAAU,CAAC,EAAE,GAAG,CAAA;KAAE;;;;;;;;;;;;;;;;;;;;IAM3C,MAAM;QACJ,OAAO,yBAA+B;YAAE,QAAQ,EAAE,GAAG,CAAC;YAAC,MAAM,EAAE,GAAG,CAAA;SAAE;QAQpE,eAAe;;IAGjB,UAAU,eAAe;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE;QAC1D,SAAS;;IAEX,iBAAiB;IACjB,cAAc,WAAW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EAEnD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE;IAEhE,SAAS;IACT,IAAI,mBAAmB;QAAE,UAAU,CAAC,EAAE,GAAG,CAAA;KAAE;;;;;;;;;;;;;;;;;;;;IAC3C,MAAM;QACJ,OAAO,iBAAuB;YAAE,QAAQ,EAAE,GAAG,CAAA;SAAE;QAQ/C,eAAe;;IAEjB,UAAU,eAAe;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE;QAC1D,SAAS;;IAEX,cAAc,WAAW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EAEnD"}
|
|
@@ -51,20 +51,28 @@ function AdminNotFound(props) {
|
|
|
51
51
|
return {
|
|
52
52
|
component: AdminPage,
|
|
53
53
|
head: ({ loaderData })=>getAdminMeta(loaderData?.metadata),
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
// `staleReloadMode: 'blocking'` (a property of the loader *object*, not a
|
|
55
|
+
// sibling route option — router-core only reads it off a non-function loader)
|
|
56
|
+
// makes stale-match revalidation await the fresh loader before committing,
|
|
57
|
+
// instead of the default background SWR that flashes the pre-navigation list
|
|
58
|
+
// (e.g. a just-created doc missing) until the reload lands.
|
|
59
|
+
loader: {
|
|
60
|
+
handler: async ({ location, params })=>{
|
|
61
|
+
const data = await runLoader(load, params._splat ?? '', location.searchStr);
|
|
62
|
+
if (data?._notFound) {
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error -- TanStack Router requires throwing notFound objects
|
|
64
|
+
throw notFound({
|
|
65
|
+
data: {
|
|
66
|
+
routeKey: data.routeKey,
|
|
67
|
+
rscPayload: data.rscPayload
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return data;
|
|
72
|
+
},
|
|
73
|
+
staleReloadMode: 'blocking'
|
|
67
74
|
},
|
|
75
|
+
// Surface query params in `loaderDeps` so `?locale=es` re-runs the loader.
|
|
68
76
|
loaderDeps: ({ search })=>({
|
|
69
77
|
searchKey: JSON.stringify(search)
|
|
70
78
|
}),
|
|
@@ -79,13 +87,16 @@ function AdminNotFound(props) {
|
|
|
79
87
|
return {
|
|
80
88
|
component: AdminPage,
|
|
81
89
|
head: ({ loaderData })=>getAdminMeta(loaderData?.metadata),
|
|
82
|
-
loader:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
loader: {
|
|
91
|
+
handler: async ({ location })=>{
|
|
92
|
+
const data = await runLoader(load, '', location.searchStr);
|
|
93
|
+
if (data?._notFound) {
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error -- TanStack Router requires throwing notFound objects
|
|
95
|
+
throw notFound();
|
|
96
|
+
}
|
|
97
|
+
return data;
|
|
98
|
+
},
|
|
99
|
+
staleReloadMode: 'blocking'
|
|
89
100
|
},
|
|
90
101
|
loaderDeps: ({ search })=>({
|
|
91
102
|
searchKey: JSON.stringify(search)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/routes/adminRoutes.tsx"],"sourcesContent":["'use client'\n\nimport { NotFoundClient } from '@payloadcms/ui'\nimport { notFound, redirect, useLoaderData } from '@tanstack/react-router'\nimport { Fragment, type ReactNode } from 'react'\n\nimport { getAdminMeta } from '../utilities/meta.js'\n\n/**\n * Loader supplied by the app — a TanStack Start `createServerFn` that delegates\n * to `loadAdminPage`. Typed loosely because the server-fn's call signature is\n * generated by the Start compiler.\n */\nexport type AdminLoad = (opts: {\n data: { _splat: string; search: Record<string, string | string[]> }\n}) => Promise<any>\n\nconst searchToRecord = (searchStr: string): Record<string, string | string[]> =>\n Object.fromEntries(new URLSearchParams(searchStr)) as Record<string, string | string[]>\n\nconst runLoader = async (load: AdminLoad, splat: string, searchStr: string) => {\n const data = await load({ data: { _splat: splat, search: searchToRecord(searchStr) } })\n if (data?._redirect) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error -- TanStack Router requires throwing redirect objects\n throw redirect({ to: data._redirect })\n }\n return data\n}\n\nfunction AdminPage() {\n // RSC Flight payload — render directly. Key by the loader-derived `routeKey`\n // (not `location.pathname`, which updates before `useLoaderData` during a\n // transition and would remount with the previous payload) so navigating to a\n // different admin page remounts the view, mirroring Next route-segment\n // semantics. Search params are excluded so search-only changes reconcile in place.\n const data = useLoaderData({ strict: false })\n return <Fragment key={data?.routeKey}>{data?.rscPayload}</Fragment>\n}\n\nfunction AdminNotFound(props: { data?: { routeKey?: string; rscPayload?: ReactNode } }) {\n const rscPayload = props?.data?.rscPayload\n if (!rscPayload) {\n return <NotFoundClient />\n }\n return <Fragment key={props?.data?.routeKey}>{rscPayload}</Fragment>\n}\n\n/**\n * Route options for the admin splat route (`/_payload/admin/$`). Renders Payload's\n * server-built NotFound page (shipped via the `notFound()` error's\n * `data.rscPayload`) for unknown/access-denied routes, falling back to the bare\n * client view.\n */\nexport function payloadAdminSplatRoute({ load }: { load: AdminLoad }) {\n return {\n component: AdminPage,\n head: ({ loaderData }: { loaderData?: any }) => getAdminMeta(loaderData?.metadata),\n //
|
|
1
|
+
{"version":3,"sources":["../../src/routes/adminRoutes.tsx"],"sourcesContent":["'use client'\n\nimport { NotFoundClient } from '@payloadcms/ui'\nimport { notFound, redirect, useLoaderData } from '@tanstack/react-router'\nimport { Fragment, type ReactNode } from 'react'\n\nimport { getAdminMeta } from '../utilities/meta.js'\n\n/**\n * Loader supplied by the app — a TanStack Start `createServerFn` that delegates\n * to `loadAdminPage`. Typed loosely because the server-fn's call signature is\n * generated by the Start compiler.\n */\nexport type AdminLoad = (opts: {\n data: { _splat: string; search: Record<string, string | string[]> }\n}) => Promise<any>\n\nconst searchToRecord = (searchStr: string): Record<string, string | string[]> =>\n Object.fromEntries(new URLSearchParams(searchStr)) as Record<string, string | string[]>\n\nconst runLoader = async (load: AdminLoad, splat: string, searchStr: string) => {\n const data = await load({ data: { _splat: splat, search: searchToRecord(searchStr) } })\n if (data?._redirect) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error -- TanStack Router requires throwing redirect objects\n throw redirect({ to: data._redirect })\n }\n return data\n}\n\nfunction AdminPage() {\n // RSC Flight payload — render directly. Key by the loader-derived `routeKey`\n // (not `location.pathname`, which updates before `useLoaderData` during a\n // transition and would remount with the previous payload) so navigating to a\n // different admin page remounts the view, mirroring Next route-segment\n // semantics. Search params are excluded so search-only changes reconcile in place.\n const data = useLoaderData({ strict: false })\n return <Fragment key={data?.routeKey}>{data?.rscPayload}</Fragment>\n}\n\nfunction AdminNotFound(props: { data?: { routeKey?: string; rscPayload?: ReactNode } }) {\n const rscPayload = props?.data?.rscPayload\n if (!rscPayload) {\n return <NotFoundClient />\n }\n return <Fragment key={props?.data?.routeKey}>{rscPayload}</Fragment>\n}\n\n/**\n * Route options for the admin splat route (`/_payload/admin/$`). Renders Payload's\n * server-built NotFound page (shipped via the `notFound()` error's\n * `data.rscPayload`) for unknown/access-denied routes, falling back to the bare\n * client view.\n */\nexport function payloadAdminSplatRoute({ load }: { load: AdminLoad }) {\n return {\n component: AdminPage,\n head: ({ loaderData }: { loaderData?: any }) => getAdminMeta(loaderData?.metadata),\n // `staleReloadMode: 'blocking'` (a property of the loader *object*, not a\n // sibling route option — router-core only reads it off a non-function loader)\n // makes stale-match revalidation await the fresh loader before committing,\n // instead of the default background SWR that flashes the pre-navigation list\n // (e.g. a just-created doc missing) until the reload lands.\n loader: {\n handler: async ({ location, params }: { location: any; params: any }) => {\n const data = await runLoader(load, params._splat ?? '', location.searchStr)\n if (data?._notFound) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error -- TanStack Router requires throwing notFound objects\n throw notFound({ data: { routeKey: data.routeKey, rscPayload: data.rscPayload } })\n }\n return data\n },\n staleReloadMode: 'blocking',\n },\n // Surface query params in `loaderDeps` so `?locale=es` re-runs the loader.\n loaderDeps: ({ search }: { search: Record<string, unknown> }) => ({\n searchKey: JSON.stringify(search),\n }),\n notFoundComponent: AdminNotFound,\n validateSearch: (search: Record<string, unknown>) => search,\n }\n}\n\n/**\n * Route options for the admin index route (`/_payload/admin/`). Same loader as the\n * splat route but throws a bare `notFound()` (no rscPayload) on miss.\n */\nexport function payloadAdminIndexRoute({ load }: { load: AdminLoad }) {\n return {\n component: AdminPage,\n head: ({ loaderData }: { loaderData?: any }) => getAdminMeta(loaderData?.metadata),\n loader: {\n handler: async ({ location }: { location: any }) => {\n const data = await runLoader(load, '', location.searchStr)\n if (data?._notFound) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error -- TanStack Router requires throwing notFound objects\n throw notFound()\n }\n return data\n },\n staleReloadMode: 'blocking',\n },\n loaderDeps: ({ search }: { search: Record<string, unknown> }) => ({\n searchKey: JSON.stringify(search),\n }),\n validateSearch: (search: Record<string, unknown>) => search,\n }\n}\n"],"names":["NotFoundClient","notFound","redirect","useLoaderData","Fragment","getAdminMeta","searchToRecord","searchStr","Object","fromEntries","URLSearchParams","runLoader","load","splat","data","_splat","search","_redirect","to","AdminPage","strict","rscPayload","routeKey","AdminNotFound","props","payloadAdminSplatRoute","component","head","loaderData","metadata","loader","handler","location","params","_notFound","staleReloadMode","loaderDeps","searchKey","JSON","stringify","notFoundComponent","validateSearch","payloadAdminIndexRoute"],"mappings":"AAAA;;AAEA,SAASA,cAAc,QAAQ,iBAAgB;AAC/C,SAASC,QAAQ,EAAEC,QAAQ,EAAEC,aAAa,QAAQ,yBAAwB;AAC1E,SAASC,QAAQ,QAAwB,QAAO;AAEhD,SAASC,YAAY,QAAQ,uBAAsB;AAWnD,MAAMC,iBAAiB,CAACC,YACtBC,OAAOC,WAAW,CAAC,IAAIC,gBAAgBH;AAEzC,MAAMI,YAAY,OAAOC,MAAiBC,OAAeN;IACvD,MAAMO,OAAO,MAAMF,KAAK;QAAEE,MAAM;YAAEC,QAAQF;YAAOG,QAAQV,eAAeC;QAAW;IAAE;IACrF,IAAIO,MAAMG,WAAW;QACnB,qHAAqH;QACrH,MAAMf,SAAS;YAAEgB,IAAIJ,KAAKG,SAAS;QAAC;IACtC;IACA,OAAOH;AACT;AAEA,SAASK;IACP,6EAA6E;IAC7E,0EAA0E;IAC1E,6EAA6E;IAC7E,uEAAuE;IACvE,mFAAmF;IACnF,MAAML,OAAOX,cAAc;QAAEiB,QAAQ;IAAM;IAC3C,qBAAO,KAAChB;kBAA+BU,MAAMO;OAAvBP,MAAMQ;AAC9B;AAEA,SAASC,cAAcC,KAA+D;IACpF,MAAMH,aAAaG,OAAOV,MAAMO;IAChC,IAAI,CAACA,YAAY;QACf,qBAAO,KAACrB;IACV;IACA,qBAAO,KAACI;kBAAsCiB;OAAxBG,OAAOV,MAAMQ;AACrC;AAEA;;;;;CAKC,GACD,OAAO,SAASG,uBAAuB,EAAEb,IAAI,EAAuB;IAClE,OAAO;QACLc,WAAWP;QACXQ,MAAM,CAAC,EAAEC,UAAU,EAAwB,GAAKvB,aAAauB,YAAYC;QACzE,0EAA0E;QAC1E,8EAA8E;QAC9E,2EAA2E;QAC3E,6EAA6E;QAC7E,4DAA4D;QAC5DC,QAAQ;YACNC,SAAS,OAAO,EAAEC,QAAQ,EAAEC,MAAM,EAAkC;gBAClE,MAAMnB,OAAO,MAAMH,UAAUC,MAAMqB,OAAOlB,MAAM,IAAI,IAAIiB,SAASzB,SAAS;gBAC1E,IAAIO,MAAMoB,WAAW;oBACnB,qHAAqH;oBACrH,MAAMjC,SAAS;wBAAEa,MAAM;4BAAEQ,UAAUR,KAAKQ,QAAQ;4BAAED,YAAYP,KAAKO,UAAU;wBAAC;oBAAE;gBAClF;gBACA,OAAOP;YACT;YACAqB,iBAAiB;QACnB;QACA,2EAA2E;QAC3EC,YAAY,CAAC,EAAEpB,MAAM,EAAuC,GAAM,CAAA;gBAChEqB,WAAWC,KAAKC,SAAS,CAACvB;YAC5B,CAAA;QACAwB,mBAAmBjB;QACnBkB,gBAAgB,CAACzB,SAAoCA;IACvD;AACF;AAEA;;;CAGC,GACD,OAAO,SAAS0B,uBAAuB,EAAE9B,IAAI,EAAuB;IAClE,OAAO;QACLc,WAAWP;QACXQ,MAAM,CAAC,EAAEC,UAAU,EAAwB,GAAKvB,aAAauB,YAAYC;QACzEC,QAAQ;YACNC,SAAS,OAAO,EAAEC,QAAQ,EAAqB;gBAC7C,MAAMlB,OAAO,MAAMH,UAAUC,MAAM,IAAIoB,SAASzB,SAAS;gBACzD,IAAIO,MAAMoB,WAAW;oBACnB,qHAAqH;oBACrH,MAAMjC;gBACR;gBACA,OAAOa;YACT;YACAqB,iBAAiB;QACnB;QACAC,YAAY,CAAC,EAAEpB,MAAM,EAAuC,GAAM,CAAA;gBAChEqB,WAAWC,KAAKC,SAAS,CAACvB;YAC5B,CAAA;QACAyB,gBAAgB,CAACzB,SAAoCA;IACvD;AACF"}
|
|
@@ -7,10 +7,15 @@ type ApiRouteHandler = (ctx: {
|
|
|
7
7
|
* (`/_payload/api/$`). The app supplies `getConfig` (an `@payload-config`
|
|
8
8
|
* import) since the package cannot resolve the consumer's config.
|
|
9
9
|
*
|
|
10
|
+
* Imported from `@payloadcms/tanstack-start/server` (not the root barrel) so
|
|
11
|
+
* the server-only `handleAPIRoute` graph never reaches the client bundle.
|
|
12
|
+
*
|
|
10
13
|
* Spread the result into a literal `server.handlers` key so TanStack Start's
|
|
11
14
|
* client compiler can statically see — and prune — the server-only route:
|
|
12
15
|
*
|
|
13
16
|
* ```ts
|
|
17
|
+
* import { payloadApiHandlers } from '@payloadcms/tanstack-start/server'
|
|
18
|
+
*
|
|
14
19
|
* export const Route = createFileRoute('/_payload/api/$')({
|
|
15
20
|
* server: {
|
|
16
21
|
* handlers: payloadApiHandlers({ getConfig: async () => (await import('@payload-config')).default }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiRoute.d.ts","sourceRoot":"","sources":["../../src/routes/apiRoute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,KAAK,eAAe,GAAG,CAAC,GAAG,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEvE
|
|
1
|
+
{"version":3,"file":"apiRoute.d.ts","sourceRoot":"","sources":["../../src/routes/apiRoute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,KAAK,eAAe,GAAG,CAAC,GAAG,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,SAAS,GACV,EAAE;IACD,SAAS,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAA;CAC1C,GAAG,MAAM,CAAC,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,EAAE,eAAe,CAAC,CAcnF"}
|
package/dist/routes/apiRoute.js
CHANGED
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
* (`/_payload/api/$`). The app supplies `getConfig` (an `@payload-config`
|
|
4
4
|
* import) since the package cannot resolve the consumer's config.
|
|
5
5
|
*
|
|
6
|
+
* Imported from `@payloadcms/tanstack-start/server` (not the root barrel) so
|
|
7
|
+
* the server-only `handleAPIRoute` graph never reaches the client bundle.
|
|
8
|
+
*
|
|
6
9
|
* Spread the result into a literal `server.handlers` key so TanStack Start's
|
|
7
10
|
* client compiler can statically see — and prune — the server-only route:
|
|
8
11
|
*
|
|
9
12
|
* ```ts
|
|
13
|
+
* import { payloadApiHandlers } from '@payloadcms/tanstack-start/server'
|
|
14
|
+
*
|
|
10
15
|
* export const Route = createFileRoute('/_payload/api/$')({
|
|
11
16
|
* server: {
|
|
12
17
|
* handlers: payloadApiHandlers({ getConfig: async () => (await import('@payload-config')).default }),
|
|
@@ -19,7 +24,7 @@
|
|
|
19
24
|
* key, leaking the config graph into the client bundle.
|
|
20
25
|
*/ export function payloadApiHandlers({ getConfig }) {
|
|
21
26
|
const handler = async ({ request })=>{
|
|
22
|
-
const { handleAPIRoute } = await import('../utilities/handleAPIRoute.js');
|
|
27
|
+
const { handleAPIRoute } = await import('../utilities/handleAPIRoute.server.js');
|
|
23
28
|
return handleAPIRoute({
|
|
24
29
|
config: await getConfig(),
|
|
25
30
|
request
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/routes/apiRoute.ts"],"sourcesContent":["import type { SanitizedConfig } from 'payload'\n\ntype ApiRouteHandler = (ctx: { request: Request }) => Promise<Response>\n\n/**\n * Builds the method handlers for the Payload REST/GraphQL catch-all\n * (`/_payload/api/$`). The app supplies `getConfig` (an `@payload-config`\n * import) since the package cannot resolve the consumer's config.\n *\n * Spread the result into a literal `server.handlers` key so TanStack Start's\n * client compiler can statically see — and prune — the server-only route:\n *\n * ```ts\n * export const Route = createFileRoute('/_payload/api/$')({\n * server: {\n * handlers: payloadApiHandlers({ getConfig: async () => (await import('@payload-config')).default }),\n * },\n * })\n * ```\n *\n * The compiler only strips a literal `server:` key from a route-options object\n * expression; wrapping the whole options object in a factory call hides the\n * key, leaking the config graph into the client bundle.\n */\nexport function payloadApiHandlers({\n getConfig,\n}: {\n getConfig: () => Promise<SanitizedConfig>\n}): Record<'DELETE' | 'GET' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT', ApiRouteHandler> {\n const handler: ApiRouteHandler = async ({ request }) => {\n const { handleAPIRoute } = await import('../utilities/handleAPIRoute.js')\n return handleAPIRoute({ config: await getConfig(), request })\n }\n\n return {\n DELETE: handler,\n GET: handler,\n OPTIONS: handler,\n PATCH: handler,\n POST: handler,\n PUT: handler,\n }\n}\n"],"names":["payloadApiHandlers","getConfig","handler","request","handleAPIRoute","config","DELETE","GET","OPTIONS","PATCH","POST","PUT"],"mappings":"AAIA
|
|
1
|
+
{"version":3,"sources":["../../src/routes/apiRoute.ts"],"sourcesContent":["import type { SanitizedConfig } from 'payload'\n\ntype ApiRouteHandler = (ctx: { request: Request }) => Promise<Response>\n\n/**\n * Builds the method handlers for the Payload REST/GraphQL catch-all\n * (`/_payload/api/$`). The app supplies `getConfig` (an `@payload-config`\n * import) since the package cannot resolve the consumer's config.\n *\n * Imported from `@payloadcms/tanstack-start/server` (not the root barrel) so\n * the server-only `handleAPIRoute` graph never reaches the client bundle.\n *\n * Spread the result into a literal `server.handlers` key so TanStack Start's\n * client compiler can statically see — and prune — the server-only route:\n *\n * ```ts\n * import { payloadApiHandlers } from '@payloadcms/tanstack-start/server'\n *\n * export const Route = createFileRoute('/_payload/api/$')({\n * server: {\n * handlers: payloadApiHandlers({ getConfig: async () => (await import('@payload-config')).default }),\n * },\n * })\n * ```\n *\n * The compiler only strips a literal `server:` key from a route-options object\n * expression; wrapping the whole options object in a factory call hides the\n * key, leaking the config graph into the client bundle.\n */\nexport function payloadApiHandlers({\n getConfig,\n}: {\n getConfig: () => Promise<SanitizedConfig>\n}): Record<'DELETE' | 'GET' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT', ApiRouteHandler> {\n const handler: ApiRouteHandler = async ({ request }) => {\n const { handleAPIRoute } = await import('../utilities/handleAPIRoute.server.js')\n return handleAPIRoute({ config: await getConfig(), request })\n }\n\n return {\n DELETE: handler,\n GET: handler,\n OPTIONS: handler,\n PATCH: handler,\n POST: handler,\n PUT: handler,\n }\n}\n"],"names":["payloadApiHandlers","getConfig","handler","request","handleAPIRoute","config","DELETE","GET","OPTIONS","PATCH","POST","PUT"],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASA,mBAAmB,EACjCC,SAAS,EAGV;IACC,MAAMC,UAA2B,OAAO,EAAEC,OAAO,EAAE;QACjD,MAAM,EAAEC,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC;QACxC,OAAOA,eAAe;YAAEC,QAAQ,MAAMJ;YAAaE;QAAQ;IAC7D;IAEA,OAAO;QACLG,QAAQJ;QACRK,KAAKL;QACLM,SAASN;QACTO,OAAOP;QACPQ,MAAMR;QACNS,KAAKT;IACP;AACF"}
|
|
@@ -18,6 +18,9 @@ export declare function payloadLayoutRoute({ load, serverFunction, }: {
|
|
|
18
18
|
serverFunction: ComponentProps<typeof RootProvider>['serverFunction'];
|
|
19
19
|
}): {
|
|
20
20
|
component: () => import("react").JSX.Element;
|
|
21
|
-
loader:
|
|
21
|
+
loader: {
|
|
22
|
+
handler: () => Promise<Record<string, unknown>>;
|
|
23
|
+
staleReloadMode: string;
|
|
24
|
+
};
|
|
22
25
|
};
|
|
23
26
|
//# sourceMappingURL=layoutRoute.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layoutRoute.d.ts","sourceRoot":"","sources":["../../src/routes/layoutRoute.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAE3C,OAAO,EAAe,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAK1D;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAE/D;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,cAAc,GACf,EAAE;IACD,IAAI,EAAE,UAAU,CAAA;IAChB,cAAc,EAAE,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC,gBAAgB,CAAC,CAAA;CACtE
|
|
1
|
+
{"version":3,"file":"layoutRoute.d.ts","sourceRoot":"","sources":["../../src/routes/layoutRoute.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAE3C,OAAO,EAAe,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAK1D;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAE/D;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,cAAc,GACf,EAAE;IACD,IAAI,EAAE,UAAU,CAAA;IAChB,cAAc,EAAE,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC,gBAAgB,CAAC,CAAA;CACtE;IAqCG,SAAS;IACT,MAAM;QAAI,OAAO;QAAgB,eAAe;;EAEnD"}
|
|
@@ -42,9 +42,15 @@ import { TanStackRouterAdapter } from '../elements/RouterAdapter/index.js';
|
|
|
42
42
|
]
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
+
// `staleReloadMode` lives on the loader *object* — router-core only reads it
|
|
46
|
+
// off a non-function loader — so stale-match revalidation blocks on the fresh
|
|
47
|
+
// loader instead of flashing stale layout data via the default background SWR.
|
|
45
48
|
return {
|
|
46
49
|
component: PayloadLayout,
|
|
47
|
-
loader:
|
|
50
|
+
loader: {
|
|
51
|
+
handler: ()=>load(),
|
|
52
|
+
staleReloadMode: 'blocking'
|
|
53
|
+
}
|
|
48
54
|
};
|
|
49
55
|
}
|
|
50
56
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/routes/layoutRoute.tsx"],"sourcesContent":["'use client'\n\nimport type { ComponentProps } from 'react'\n\nimport { ProgressBar, RootProvider } from '@payloadcms/ui'\nimport { Outlet, useLoaderData } from '@tanstack/react-router'\n\nimport { TanStackRouterAdapter } from '../elements/RouterAdapter/index.js'\n\n/**\n * Loader supplied by the app — a TanStack Start `createServerFn` that delegates\n * to `loadLayoutData`. Typed loosely because the server-fn's call signature is\n * generated by the Start compiler.\n */\nexport type LayoutLoad = () => Promise<Record<string, unknown>>\n\n/**\n * Route options for the Payload admin layout (`/_payload`). Maps the layout\n * loader data onto `RootProvider` and renders the admin chrome (progress bar,\n * custom-provider tree or router `<Outlet />`, portal mount). The app supplies\n * `load` (the layout-data server fn) and `serverFunction` (the server-function\n * client wired into `RootProvider`); everything else is adapter-owned.\n */\nexport function payloadLayoutRoute({\n load,\n serverFunction,\n}: {\n load: LayoutLoad\n serverFunction: ComponentProps<typeof RootProvider>['serverFunction']\n}) {\n function PayloadLayout() {\n const data = useLoaderData({ strict: false })\n\n return (\n <>\n <RootProvider\n config={data.clientConfig}\n dateFNSKey={data.dateFNSKey}\n fallbackLang={data.fallbackLang}\n highContrastMode={false}\n isNavOpen={data.isNavOpen}\n languageCode={data.languageCode}\n languageOptions={data.languageOptions}\n locale={data.locale}\n permissions={data.user ? data.permissions : null}\n RouterAdapter={TanStackRouterAdapter}\n serverFunction={serverFunction}\n theme={data.theme}\n translations={data.translations}\n user={data.user}\n >\n <ProgressBar />\n {/* `data.providers` is the custom-provider tree (config.admin.components.providers)\n already wrapping the router <Outlet />; falls back to a bare <Outlet /> when\n no custom providers are configured. */}\n {data.providers ?? <Outlet />}\n </RootProvider>\n <div id=\"portal\" />\n </>\n )\n }\n\n return {
|
|
1
|
+
{"version":3,"sources":["../../src/routes/layoutRoute.tsx"],"sourcesContent":["'use client'\n\nimport type { ComponentProps } from 'react'\n\nimport { ProgressBar, RootProvider } from '@payloadcms/ui'\nimport { Outlet, useLoaderData } from '@tanstack/react-router'\n\nimport { TanStackRouterAdapter } from '../elements/RouterAdapter/index.js'\n\n/**\n * Loader supplied by the app — a TanStack Start `createServerFn` that delegates\n * to `loadLayoutData`. Typed loosely because the server-fn's call signature is\n * generated by the Start compiler.\n */\nexport type LayoutLoad = () => Promise<Record<string, unknown>>\n\n/**\n * Route options for the Payload admin layout (`/_payload`). Maps the layout\n * loader data onto `RootProvider` and renders the admin chrome (progress bar,\n * custom-provider tree or router `<Outlet />`, portal mount). The app supplies\n * `load` (the layout-data server fn) and `serverFunction` (the server-function\n * client wired into `RootProvider`); everything else is adapter-owned.\n */\nexport function payloadLayoutRoute({\n load,\n serverFunction,\n}: {\n load: LayoutLoad\n serverFunction: ComponentProps<typeof RootProvider>['serverFunction']\n}) {\n function PayloadLayout() {\n const data = useLoaderData({ strict: false })\n\n return (\n <>\n <RootProvider\n config={data.clientConfig}\n dateFNSKey={data.dateFNSKey}\n fallbackLang={data.fallbackLang}\n highContrastMode={false}\n isNavOpen={data.isNavOpen}\n languageCode={data.languageCode}\n languageOptions={data.languageOptions}\n locale={data.locale}\n permissions={data.user ? data.permissions : null}\n RouterAdapter={TanStackRouterAdapter}\n serverFunction={serverFunction}\n theme={data.theme}\n translations={data.translations}\n user={data.user}\n >\n <ProgressBar />\n {/* `data.providers` is the custom-provider tree (config.admin.components.providers)\n already wrapping the router <Outlet />; falls back to a bare <Outlet /> when\n no custom providers are configured. */}\n {data.providers ?? <Outlet />}\n </RootProvider>\n <div id=\"portal\" />\n </>\n )\n }\n\n // `staleReloadMode` lives on the loader *object* — router-core only reads it\n // off a non-function loader — so stale-match revalidation blocks on the fresh\n // loader instead of flashing stale layout data via the default background SWR.\n return {\n component: PayloadLayout,\n loader: { handler: () => load(), staleReloadMode: 'blocking' },\n }\n}\n"],"names":["ProgressBar","RootProvider","Outlet","useLoaderData","TanStackRouterAdapter","payloadLayoutRoute","load","serverFunction","PayloadLayout","data","strict","config","clientConfig","dateFNSKey","fallbackLang","highContrastMode","isNavOpen","languageCode","languageOptions","locale","permissions","user","RouterAdapter","theme","translations","providers","div","id","component","loader","handler","staleReloadMode"],"mappings":"AAAA;;AAIA,SAASA,WAAW,EAAEC,YAAY,QAAQ,iBAAgB;AAC1D,SAASC,MAAM,EAAEC,aAAa,QAAQ,yBAAwB;AAE9D,SAASC,qBAAqB,QAAQ,qCAAoC;AAS1E;;;;;;CAMC,GACD,OAAO,SAASC,mBAAmB,EACjCC,IAAI,EACJC,cAAc,EAIf;IACC,SAASC;QACP,MAAMC,OAAON,cAAc;YAAEO,QAAQ;QAAM;QAE3C,qBACE;;8BACE,MAACT;oBACCU,QAAQF,KAAKG,YAAY;oBACzBC,YAAYJ,KAAKI,UAAU;oBAC3BC,cAAcL,KAAKK,YAAY;oBAC/BC,kBAAkB;oBAClBC,WAAWP,KAAKO,SAAS;oBACzBC,cAAcR,KAAKQ,YAAY;oBAC/BC,iBAAiBT,KAAKS,eAAe;oBACrCC,QAAQV,KAAKU,MAAM;oBACnBC,aAAaX,KAAKY,IAAI,GAAGZ,KAAKW,WAAW,GAAG;oBAC5CE,eAAelB;oBACfG,gBAAgBA;oBAChBgB,OAAOd,KAAKc,KAAK;oBACjBC,cAAcf,KAAKe,YAAY;oBAC/BH,MAAMZ,KAAKY,IAAI;;sCAEf,KAACrB;wBAIAS,KAAKgB,SAAS,kBAAI,KAACvB;;;8BAEtB,KAACwB;oBAAIC,IAAG;;;;IAGd;IAEA,6EAA6E;IAC7E,8EAA8E;IAC9E,+EAA+E;IAC/E,OAAO;QACLC,WAAWpB;QACXqB,QAAQ;YAAEC,SAAS,IAAMxB;YAAQyB,iBAAiB;QAAW;IAC/D;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphqlHandler.server.d.ts","sourceRoot":"","sources":["../../src/utilities/graphqlHandler.server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA2B,eAAe,EAAE,MAAM,SAAS,CAAA;AAyFvE;;;;;GAKG;AACH,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"graphqlHandler.server.d.ts","sourceRoot":"","sources":["../../src/utilities/graphqlHandler.server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA2B,eAAe,EAAE,MAAM,SAAS,CAAA;AAyFvE;;;;;GAKG;AACH,eAAO,MAAM,aAAa,yBAGvB;IACD,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,CAAA;IAClD,OAAO,EAAE,OAAO,CAAA;CACjB,KAAG,OAAO,CAAC,QAAQ,CA2DnB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handleAPIRoute.server.d.ts","sourceRoot":"","sources":["../../src/utilities/handleAPIRoute.server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAI9C;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,EACnC,MAAM,EACN,OAAO,GACR,EAAE;IACD,MAAM,EAAE,eAAe,CAAA;IACvB,OAAO,EAAE,OAAO,CAAA;CACjB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAapB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/handleAPIRoute.server.ts"],"sourcesContent":["import type { SanitizedConfig } from 'payload'\n\nimport { handleEndpoints } from 'payload'\n\n/**\n * Routes a TanStack Start API request (`/api/*`) to Payload's REST/GraphQL\n * endpoint handler. The framework adapter wires this into the `/api/$` route's\n * server handlers, supplying the app's resolved `config`.\n */\nexport async function handleAPIRoute({\n config,\n request,\n}: {\n config: SanitizedConfig\n request: Request\n}): Promise<Response> {\n const url = new URL(request.url)\n const slugParts = url.pathname\n .replace(/^\\/api\\/?/, '')\n .split('/')\n .filter(Boolean)\n const path = slugParts.length ? `/api/${slugParts.join('/')}` : '/api'\n\n return handleEndpoints({\n config,\n path,\n request,\n })\n}\n"],"names":["handleEndpoints","handleAPIRoute","config","request","url","URL","slugParts","pathname","replace","split","filter","Boolean","path","length","join"],"mappings":"AAEA,SAASA,eAAe,QAAQ,UAAS;AAEzC;;;;CAIC,GACD,OAAO,eAAeC,eAAe,EACnCC,MAAM,EACNC,OAAO,EAIR;IACC,MAAMC,MAAM,IAAIC,IAAIF,QAAQC,GAAG;IAC/B,MAAME,YAAYF,IAAIG,QAAQ,CAC3BC,OAAO,CAAC,aAAa,IACrBC,KAAK,CAAC,KACNC,MAAM,CAACC;IACV,MAAMC,OAAON,UAAUO,MAAM,GAAG,CAAC,KAAK,EAAEP,UAAUQ,IAAI,CAAC,MAAM,GAAG;IAEhE,OAAOd,gBAAgB;QACrBE;QACAU;QACAT;IACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadAdminPage.d.ts","sourceRoot":"","sources":["../../src/utilities/loadAdminPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAc,eAAe,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"loadAdminPage.d.ts","sourceRoot":"","sources":["../../src/utilities/loadAdminPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAc,eAAe,EAAE,MAAM,SAAS,CAAA;AAKrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAMlD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,eAAe,CAAA;IACvB,SAAS,EAAE,SAAS,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAA;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,mBAAmB,GAC3B;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,GACpE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GACrB;IACE,QAAQ,EAAE,iBAAiB,CAAA;IAC3B;;;;;;;;;;OAUG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,KAAK,CAAC,SAAS,CAAA;CAC5B,CAAA;AA0FL;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,aAAa,CAAC,EAClC,MAAM,EACN,SAAS,EACT,MAAM,EACN,KAAK,GACN,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA2JlD"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getViewportContent } from '@payloadcms/ui/shared';
|
|
1
2
|
import { renderServerComponent } from '@tanstack/react-start/rsc';
|
|
2
3
|
import { getRequestI18n } from './getRequestI18n.server.js';
|
|
3
4
|
import { initReq } from './initReq.server.js';
|
|
@@ -99,16 +100,21 @@ const resolveTitle = (title)=>{
|
|
|
99
100
|
// by RSC streaming deep inside view components). Read after the render.
|
|
100
101
|
const nav = {};
|
|
101
102
|
const pageServerAdapter = createPageRenderServerAdapter(nav);
|
|
103
|
+
let userAgent;
|
|
102
104
|
// `renderRoot` calls `initReq` itself with its own overrides (query
|
|
103
105
|
// re-nesting, `urlSuffix`, `fallbackLocale`). Forward them, injecting the
|
|
104
106
|
// page-render `ServerAdapter` so `req.server.redirect()` / `.notFound()`
|
|
105
107
|
// is recorded + thrown rather than escaping as raw TanStack nav.
|
|
106
|
-
const boundInitReq = (args)=>
|
|
108
|
+
const boundInitReq = async (args)=>{
|
|
109
|
+
const result = await initReq({
|
|
107
110
|
configPromise: args.configPromise,
|
|
108
111
|
importMap: args.importMap,
|
|
109
112
|
overrides: args.overrides,
|
|
110
113
|
serverAdapter: pageServerAdapter
|
|
111
114
|
});
|
|
115
|
+
userAgent = result.headers.get('user-agent') ?? undefined;
|
|
116
|
+
return result;
|
|
117
|
+
};
|
|
112
118
|
const notFound = ()=>{
|
|
113
119
|
nav.type = 'notFound';
|
|
114
120
|
throw new Error('not-found');
|
|
@@ -169,6 +175,19 @@ const resolveTitle = (title)=>{
|
|
|
169
175
|
searchParams: Promise.resolve(searchParams)
|
|
170
176
|
});
|
|
171
177
|
const rscPayload = await renderServerComponent(node);
|
|
178
|
+
// The server-function (client-nav RPC) path returns the flight stream unread —
|
|
179
|
+
// unlike the router/SSR path, which awaits a decode that drives the render to
|
|
180
|
+
// completion. So streamed side effects (e.g. DocumentView's autosave-create
|
|
181
|
+
// `server.redirect()`) haven't run yet and `nav` is empty. Buffer the stream to
|
|
182
|
+
// completion to force the render (populating `nav`), then hand the client a fresh
|
|
183
|
+
// replayable stream from that buffer.
|
|
184
|
+
if (!nav.type) {
|
|
185
|
+
const wrapper = rscPayload[Symbol.for('tanstack.rsc.stream')];
|
|
186
|
+
if (typeof wrapper?.createReplayStream === 'function') {
|
|
187
|
+
const buffer = await new Response(wrapper.createReplayStream()).arrayBuffer();
|
|
188
|
+
wrapper.createReplayStream = ()=>new Response(buffer).body;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
172
191
|
// Navigation thrown deep inside a streamed view component (e.g. access
|
|
173
192
|
// denied → notFound, already-authenticated → redirect) is swallowed into
|
|
174
193
|
// the RSC stream and never rejects the render. Honor it from the holder,
|
|
@@ -196,7 +215,10 @@ const resolveTitle = (title)=>{
|
|
|
196
215
|
}
|
|
197
216
|
});
|
|
198
217
|
return {
|
|
199
|
-
metadata:
|
|
218
|
+
metadata: {
|
|
219
|
+
...toAdminPageMetadata(meta),
|
|
220
|
+
viewport: getViewportContent(userAgent)
|
|
221
|
+
},
|
|
200
222
|
routeKey: splat ?? '',
|
|
201
223
|
rscPayload
|
|
202
224
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/loadAdminPage.tsx"],"sourcesContent":["import type { ImportMap, MetaConfig, SanitizedConfig } from 'payload'\n\nimport { renderServerComponent } from '@tanstack/react-start/rsc'\n\nimport type { AdminPageMetadata } from './meta.js'\n\nimport { getRequestI18n } from './getRequestI18n.server.js'\nimport { initReq } from './initReq.server.js'\nimport { createPageRenderServerAdapter } from './serverAdapter.server.js'\n\nexport type LoadAdminPageArgs = {\n config: SanitizedConfig\n importMap: ImportMap\n search?: Record<string, string | string[]>\n splat?: string\n}\n\nexport type LoadAdminPageResult =\n | { _notFound: true; routeKey?: string; rscPayload?: React.ReactNode }\n | { _redirect: string }\n | {\n metadata: AdminPageMetadata\n /**\n * Stable identity for this rendered route (the splat, i.e. the path\n * after `/admin/`). The client keys the rendered subtree by this so the\n * view remounts exactly when a new payload arrives. Keying by\n * `location.pathname` instead races: the pathname updates before\n * `useLoaderData()` during a transition, so the subtree would remount\n * with the *previous* payload and then reconcile the fresh payload in\n * place — leaving client providers (DocumentInfo, etc.) holding stale\n * `useState` values from the prior document. Search params are excluded\n * so search-only changes (e.g. list-view filtering) reconcile in place.\n */\n routeKey: string\n rscPayload: React.ReactNode\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\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 */\nconst 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\n/**\n * Renders an admin page for TanStack Start and returns a serializable loader\n * result. The framework adapter wraps this in a `createServerFn` that supplies\n * the app's `config` and generated `importMap`.\n *\n * 1. Initializes the Payload request via the shared `renderRoot` orchestrator\n * from `@payloadcms/ui`, passing a TanStack-bound `initReq`. The injected\n * page-render `ServerAdapter` records navigation intent and throws the\n * framework-agnostic error contract.\n * 2. Pipes the resulting React server tree through `renderServerComponent`\n * to produce a Flight payload the client consumes directly.\n * 3. Resolves page metadata via the same shared `generatePageMetadata`\n * Next.js uses, returning plain serializable strings for `head()`.\n *\n * Navigation surfaces two ways and both become a `_notFound` / `_redirect`\n * sentinel the route loader re-throws as native TanStack nav:\n * - thrown during `renderRoot` orchestration (e.g. the login redirect) → caught\n * by the try/catch below;\n * - thrown deep inside a streamed view component (e.g. `DocumentView` access\n * denied, `LoginView` already-authenticated) → swallowed into the RSC stream,\n * so it's read from the `nav` holder after `renderServerComponent` resolves.\n */\nexport async function loadAdminPage({\n config,\n importMap,\n search,\n splat,\n}: LoadAdminPageArgs): Promise<LoadAdminPageResult> {\n const { renderRoot } = await import('@payloadcms/ui/views/Root')\n const { defaultAdminViews } = await import('@payloadcms/ui/views/Root/adminViews')\n const { generatePageMetadata } = await import('@payloadcms/ui/views/Root/generatePageMetadata')\n\n const splatSegments = splat ? splat.split('/').filter(Boolean) : []\n // Match Next's optional-catch-all behavior: the admin root (`/admin`) has no\n // segments. Passing an empty array makes the shared `renderRoot` build\n // `currentRoute` as `/admin/` (trailing slash), which no longer equals\n // `adminRoute` and causes `handleAuthRedirect` to append `?redirect=/admin/`.\n // Passing `undefined` yields `currentRoute = /admin`, so the unauthenticated\n // redirect lands on a clean `/admin/login`.\n const segments = splatSegments.length > 0 ? splatSegments : undefined\n const searchParams = search ?? {}\n\n // Records navigation requested via `req.server.*` (including throws swallowed\n // by RSC streaming deep inside view components). Read after the render.\n const nav: { type?: 'notFound' | 'redirect'; url?: string } = {}\n const pageServerAdapter = createPageRenderServerAdapter(nav)\n\n // `renderRoot` calls `initReq` itself with its own overrides (query\n // re-nesting, `urlSuffix`, `fallbackLocale`). Forward them, injecting the\n // page-render `ServerAdapter` so `req.server.redirect()` / `.notFound()`\n // is recorded + thrown rather than escaping as raw TanStack nav.\n const boundInitReq: Parameters<typeof renderRoot>[0]['initReq'] = (args) =>\n initReq({\n configPromise: args.configPromise,\n importMap: args.importMap,\n overrides: args.overrides,\n serverAdapter: pageServerAdapter,\n })\n\n const notFound = (): never => {\n nav.type = 'notFound'\n throw new Error('not-found')\n }\n const redirect = (url: string): never => {\n nav.type = 'redirect'\n nav.url = url\n throw new Error(`redirect:${url}`)\n }\n\n // Build the 404 result the route loader re-throws as TanStack `notFound()`.\n //\n // Throwing `notFound()` is the only way to set the SSR document status to 404\n // (it's read from `router.stores.statusCode`, set by a not-found match — NOT\n // from `setResponseStatus`, which only affects the RSC RPC response). But the\n // matching `notFoundComponent` is a client component with no access to the\n // Payload `req`, so it can't build the admin chrome on its own. To match Next\n // (whose not-found route renders the full admin layout — nav sidebar, etc. —\n // around the NotFound body, see `renderNotFoundPage`), we render that same\n // shared `renderNotFoundPage` tree here, server-side, and ship its Flight\n // payload through the `notFound()` error so the client renders it verbatim.\n // For users without admin access `renderNotFoundPage` returns the bare\n // `NotFoundClient`, preserving the access-denied behavior.\n const renderNotFound = async (): Promise<LoadAdminPageResult> => {\n const { renderNotFoundPage } = await import('@payloadcms/ui/views/NotFound/page')\n\n const notFoundNode = await renderNotFoundPage({\n config: Promise.resolve(config),\n importMap,\n initReq: (args) =>\n initReq({\n configPromise: args.configPromise,\n importMap: args.importMap,\n overrides: args.overrides,\n }),\n params: Promise.resolve({ segments: splatSegments }),\n searchParams: Promise.resolve(searchParams),\n })\n\n const rscPayload = await renderServerComponent(notFoundNode as React.ReactElement)\n\n return { _notFound: true, routeKey: splat ?? '', rscPayload }\n }\n\n try {\n const node = await renderRoot({\n adminViews: defaultAdminViews,\n config: Promise.resolve(config),\n importMap,\n initReq: boundInitReq,\n notFound,\n // `segments` is intentionally `undefined` for the admin root (`/admin`),\n // matching Next's optional catch-all; `renderRoot` handles it at runtime.\n params: Promise.resolve({ segments }) as Parameters<typeof renderRoot>[0]['params'],\n redirect,\n searchParams: Promise.resolve(searchParams),\n })\n\n const rscPayload = await renderServerComponent(node as React.ReactElement)\n\n // Navigation thrown deep inside a streamed view component (e.g. access\n // denied → notFound, already-authenticated → redirect) is swallowed into\n // the RSC stream and never rejects the render. Honor it from the holder,\n // discarding the (broken) payload from the aborted render.\n if (nav.type === 'redirect' && nav.url) {\n return { _redirect: nav.url }\n }\n if (nav.type === 'notFound') {\n return await renderNotFound()\n }\n\n // Resolve metadata through the same shared generator Next.js uses. Only\n // plain strings cross the wire (the full `MetaConfig` carries a `URL`\n // `metadataBase` and icons that seroval cannot serialize).\n const i18n = await getRequestI18n({ config })\n const meta = await generatePageMetadata({\n adminViews: defaultAdminViews as Parameters<typeof generatePageMetadata>[0]['adminViews'],\n config,\n i18n,\n params: { segments },\n })\n\n return {\n metadata: toAdminPageMetadata(meta),\n routeKey: splat ?? '',\n rscPayload,\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n if (nav.type === 'notFound' || message === 'not-found') {\n return await renderNotFound()\n }\n if (nav.type === 'redirect' || message.startsWith('redirect:')) {\n return { _redirect: nav.url ?? message.slice('redirect:'.length) }\n }\n throw err\n }\n}\n"],"names":["renderServerComponent","getRequestI18n","initReq","createPageRenderServerAdapter","resolveTitle","title","undefined","absolute","default","toAdminPageMetadata","meta","og","openGraph","rawImages","images","imagesArray","Array","isArray","map","image","url","alt","height","String","width","filter","Boolean","rawIcons","icons","iconList","icon","rel","type","media","sizes","keywords","description","length","join","siteName","robots","loadAdminPage","config","importMap","search","splat","renderRoot","defaultAdminViews","generatePageMetadata","splatSegments","split","segments","searchParams","nav","pageServerAdapter","boundInitReq","args","configPromise","overrides","serverAdapter","notFound","Error","redirect","renderNotFound","renderNotFoundPage","notFoundNode","Promise","resolve","params","rscPayload","_notFound","routeKey","node","adminViews","_redirect","i18n","metadata","err","message","startsWith","slice"],"mappings":"AAEA,SAASA,qBAAqB,QAAQ,4BAA2B;AAIjE,SAASC,cAAc,QAAQ,6BAA4B;AAC3D,SAASC,OAAO,QAAQ,sBAAqB;AAC7C,SAASC,6BAA6B,QAAQ,4BAA2B;AA6BzE,MAAMC,eAAe,CAACC;IACpB,IAAI,CAACA,OAAO;QACV,OAAOC;IACT;IACA,IAAI,OAAOD,UAAU,UAAU;QAC7B,OAAOA;IACT;IACA,IAAI,cAAcA,OAAO;QACvB,OAAOA,MAAME,QAAQ;IACvB;IACA,OAAOF,MAAMG,OAAO;AACtB;AAEA;;;;;;CAMC,GACD,MAAMC,sBAAsB,CAACC;IAC3B,MAAMC,KAAKD,KAAKE,SAAS;IASzB,MAAMC,YAAYF,IAAIG;IACtB,MAAMC,cAAcF,YAAaG,MAAMC,OAAO,CAACJ,aAAaA,YAAY;QAACA;KAAU,GAAI,EAAE;IACzF,MAAMC,SAASC,YACZG,GAAG,CAAC,CAACC,QACJ,OAAOA,UAAU,WACb;YAAEC,KAAKD;QAAM,IACbA,OAAOC,MACL;YAAEC,KAAKF,MAAME,GAAG;YAAEC,QAAQH,MAAMG,MAAM;YAAEF,KAAKG,OAAOJ,MAAMC,GAAG;YAAGI,OAAOL,MAAMK,KAAK;QAAC,IACnFlB,WAEPmB,MAAM,CAACC;IAEV,MAAMC,WAAWjB,KAAKkB,KAAK;IAC3B,MAAMC,WAAWb,MAAMC,OAAO,CAACU,YAC3BA,WACAA,YAAY,OAAOA,aAAa,YAAYX,MAAMC,OAAO,CAACU,SAASG,IAAI,IACrEH,SAASG,IAAI,GACb,EAAE;IACR,MAAMF,QAAQC,SACXX,GAAG,CAAC,CAACY,OACJ,OAAOA,SAAS,WACZ;YAAEC,KAAK;YAAQX,KAAKU;QAAK,IACzBA,MAAMV,MACJ;YACEY,MAAMF,KAAKE,IAAI;YACfC,OAAOH,KAAKG,KAAK;YACjBF,KAAKD,KAAKC,GAAG,IAAI;YACjBG,OAAOJ,KAAKI,KAAK;YACjBd,KAAKG,OAAOO,KAAKV,GAAG;QACtB,IACAd,WAEPmB,MAAM,CAACC;IAEV,MAAMS,WAAWzB,KAAKyB,QAAQ;IAE9B,OAAO;QACLC,aAAa,OAAO1B,KAAK0B,WAAW,KAAK,WAAW1B,KAAK0B,WAAW,GAAG9B;QACvEsB,OAAOA,OAAOS,SAAST,QAAQtB;QAC/B6B,UACE,OAAOA,aAAa,WAChBA,WACAnB,MAAMC,OAAO,CAACkB,YACZA,SAASG,IAAI,CAAC,QACdhC;QACRM,WAAWD,KACP;YACEyB,aAAa,OAAOzB,GAAGyB,WAAW,KAAK,WAAWzB,GAAGyB,WAAW,GAAG9B;YACnEQ,QAAQA,QAAQuB,SAASvB,SAASR;YAClCiC,UAAU,OAAO5B,GAAG4B,QAAQ,KAAK,WAAW5B,GAAG4B,QAAQ,GAAGjC;YAC1DD,OAAO,OAAOM,GAAGN,KAAK,KAAK,WAAWM,GAAGN,KAAK,GAAGC;QACnD,IACAA;QACJkC,QAAQ,OAAO9B,KAAK8B,MAAM,KAAK,WAAW9B,KAAK8B,MAAM,GAAGlC;QACxDD,OAAOD,aAAaM,KAAKL,KAAK;IAChC;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;CAqBC,GACD,OAAO,eAAeoC,cAAc,EAClCC,MAAM,EACNC,SAAS,EACTC,MAAM,EACNC,KAAK,EACa;IAClB,MAAM,EAAEC,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC;IACpC,MAAM,EAAEC,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC;IAC3C,MAAM,EAAEC,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC;IAE9C,MAAMC,gBAAgBJ,QAAQA,MAAMK,KAAK,CAAC,KAAKzB,MAAM,CAACC,WAAW,EAAE;IACnE,6EAA6E;IAC7E,uEAAuE;IACvE,uEAAuE;IACvE,8EAA8E;IAC9E,6EAA6E;IAC7E,4CAA4C;IAC5C,MAAMyB,WAAWF,cAAcZ,MAAM,GAAG,IAAIY,gBAAgB3C;IAC5D,MAAM8C,eAAeR,UAAU,CAAC;IAEhC,8EAA8E;IAC9E,wEAAwE;IACxE,MAAMS,MAAwD,CAAC;IAC/D,MAAMC,oBAAoBnD,8BAA8BkD;IAExD,oEAAoE;IACpE,0EAA0E;IAC1E,yEAAyE;IACzE,iEAAiE;IACjE,MAAME,eAA4D,CAACC,OACjEtD,QAAQ;YACNuD,eAAeD,KAAKC,aAAa;YACjCd,WAAWa,KAAKb,SAAS;YACzBe,WAAWF,KAAKE,SAAS;YACzBC,eAAeL;QACjB;IAEF,MAAMM,WAAW;QACfP,IAAIrB,IAAI,GAAG;QACX,MAAM,IAAI6B,MAAM;IAClB;IACA,MAAMC,WAAW,CAAC1C;QAChBiC,IAAIrB,IAAI,GAAG;QACXqB,IAAIjC,GAAG,GAAGA;QACV,MAAM,IAAIyC,MAAM,CAAC,SAAS,EAAEzC,KAAK;IACnC;IAEA,4EAA4E;IAC5E,EAAE;IACF,8EAA8E;IAC9E,6EAA6E;IAC7E,8EAA8E;IAC9E,2EAA2E;IAC3E,8EAA8E;IAC9E,6EAA6E;IAC7E,2EAA2E;IAC3E,0EAA0E;IAC1E,4EAA4E;IAC5E,uEAAuE;IACvE,2DAA2D;IAC3D,MAAM2C,iBAAiB;QACrB,MAAM,EAAEC,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC;QAE5C,MAAMC,eAAe,MAAMD,mBAAmB;YAC5CtB,QAAQwB,QAAQC,OAAO,CAACzB;YACxBC;YACAzC,SAAS,CAACsD,OACRtD,QAAQ;oBACNuD,eAAeD,KAAKC,aAAa;oBACjCd,WAAWa,KAAKb,SAAS;oBACzBe,WAAWF,KAAKE,SAAS;gBAC3B;YACFU,QAAQF,QAAQC,OAAO,CAAC;gBAAEhB,UAAUF;YAAc;YAClDG,cAAcc,QAAQC,OAAO,CAACf;QAChC;QAEA,MAAMiB,aAAa,MAAMrE,sBAAsBiE;QAE/C,OAAO;YAAEK,WAAW;YAAMC,UAAU1B,SAAS;YAAIwB;QAAW;IAC9D;IAEA,IAAI;QACF,MAAMG,OAAO,MAAM1B,WAAW;YAC5B2B,YAAY1B;YACZL,QAAQwB,QAAQC,OAAO,CAACzB;YACxBC;YACAzC,SAASqD;YACTK;YACA,yEAAyE;YACzE,0EAA0E;YAC1EQ,QAAQF,QAAQC,OAAO,CAAC;gBAAEhB;YAAS;YACnCW;YACAV,cAAcc,QAAQC,OAAO,CAACf;QAChC;QAEA,MAAMiB,aAAa,MAAMrE,sBAAsBwE;QAE/C,uEAAuE;QACvE,yEAAyE;QACzE,yEAAyE;QACzE,2DAA2D;QAC3D,IAAInB,IAAIrB,IAAI,KAAK,cAAcqB,IAAIjC,GAAG,EAAE;YACtC,OAAO;gBAAEsD,WAAWrB,IAAIjC,GAAG;YAAC;QAC9B;QACA,IAAIiC,IAAIrB,IAAI,KAAK,YAAY;YAC3B,OAAO,MAAM+B;QACf;QAEA,wEAAwE;QACxE,sEAAsE;QACtE,2DAA2D;QAC3D,MAAMY,OAAO,MAAM1E,eAAe;YAAEyC;QAAO;QAC3C,MAAMhC,OAAO,MAAMsC,qBAAqB;YACtCyB,YAAY1B;YACZL;YACAiC;YACAP,QAAQ;gBAAEjB;YAAS;QACrB;QAEA,OAAO;YACLyB,UAAUnE,oBAAoBC;YAC9B6D,UAAU1B,SAAS;YACnBwB;QACF;IACF,EAAE,OAAOQ,KAAK;QACZ,MAAMC,UAAUD,eAAehB,QAAQgB,IAAIC,OAAO,GAAGvD,OAAOsD;QAC5D,IAAIxB,IAAIrB,IAAI,KAAK,cAAc8C,YAAY,aAAa;YACtD,OAAO,MAAMf;QACf;QACA,IAAIV,IAAIrB,IAAI,KAAK,cAAc8C,QAAQC,UAAU,CAAC,cAAc;YAC9D,OAAO;gBAAEL,WAAWrB,IAAIjC,GAAG,IAAI0D,QAAQE,KAAK,CAAC,YAAY3C,MAAM;YAAE;QACnE;QACA,MAAMwC;IACR;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/loadAdminPage.tsx"],"sourcesContent":["import type { ImportMap, MetaConfig, SanitizedConfig } from 'payload'\n\nimport { getViewportContent } from '@payloadcms/ui/shared'\nimport { renderServerComponent } from '@tanstack/react-start/rsc'\n\nimport type { AdminPageMetadata } from './meta.js'\n\nimport { getRequestI18n } from './getRequestI18n.server.js'\nimport { initReq } from './initReq.server.js'\nimport { createPageRenderServerAdapter } from './serverAdapter.server.js'\n\nexport type LoadAdminPageArgs = {\n config: SanitizedConfig\n importMap: ImportMap\n search?: Record<string, string | string[]>\n splat?: string\n}\n\nexport type LoadAdminPageResult =\n | { _notFound: true; routeKey?: string; rscPayload?: React.ReactNode }\n | { _redirect: string }\n | {\n metadata: AdminPageMetadata\n /**\n * Stable identity for this rendered route (the splat, i.e. the path\n * after `/admin/`). The client keys the rendered subtree by this so the\n * view remounts exactly when a new payload arrives. Keying by\n * `location.pathname` instead races: the pathname updates before\n * `useLoaderData()` during a transition, so the subtree would remount\n * with the *previous* payload and then reconcile the fresh payload in\n * place — leaving client providers (DocumentInfo, etc.) holding stale\n * `useState` values from the prior document. Search params are excluded\n * so search-only changes (e.g. list-view filtering) reconcile in place.\n */\n routeKey: string\n rscPayload: React.ReactNode\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\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 */\nconst 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\n/**\n * Renders an admin page for TanStack Start and returns a serializable loader\n * result. The framework adapter wraps this in a `createServerFn` that supplies\n * the app's `config` and generated `importMap`.\n *\n * 1. Initializes the Payload request via the shared `renderRoot` orchestrator\n * from `@payloadcms/ui`, passing a TanStack-bound `initReq`. The injected\n * page-render `ServerAdapter` records navigation intent and throws the\n * framework-agnostic error contract.\n * 2. Pipes the resulting React server tree through `renderServerComponent`\n * to produce a Flight payload the client consumes directly.\n * 3. Resolves page metadata via the same shared `generatePageMetadata`\n * Next.js uses, returning plain serializable strings for `head()`.\n *\n * Navigation surfaces two ways and both become a `_notFound` / `_redirect`\n * sentinel the route loader re-throws as native TanStack nav:\n * - thrown during `renderRoot` orchestration (e.g. the login redirect) → caught\n * by the try/catch below;\n * - thrown deep inside a streamed view component (e.g. `DocumentView` access\n * denied, `LoginView` already-authenticated) → swallowed into the RSC stream,\n * so it's read from the `nav` holder after `renderServerComponent` resolves.\n */\nexport async function loadAdminPage({\n config,\n importMap,\n search,\n splat,\n}: LoadAdminPageArgs): Promise<LoadAdminPageResult> {\n const { renderRoot } = await import('@payloadcms/ui/views/Root')\n const { defaultAdminViews } = await import('@payloadcms/ui/views/Root/adminViews')\n const { generatePageMetadata } = await import('@payloadcms/ui/views/Root/generatePageMetadata')\n\n const splatSegments = splat ? splat.split('/').filter(Boolean) : []\n // Match Next's optional-catch-all behavior: the admin root (`/admin`) has no\n // segments. Passing an empty array makes the shared `renderRoot` build\n // `currentRoute` as `/admin/` (trailing slash), which no longer equals\n // `adminRoute` and causes `handleAuthRedirect` to append `?redirect=/admin/`.\n // Passing `undefined` yields `currentRoute = /admin`, so the unauthenticated\n // redirect lands on a clean `/admin/login`.\n const segments = splatSegments.length > 0 ? splatSegments : undefined\n const searchParams = search ?? {}\n\n // Records navigation requested via `req.server.*` (including throws swallowed\n // by RSC streaming deep inside view components). Read after the render.\n const nav: { type?: 'notFound' | 'redirect'; url?: string } = {}\n const pageServerAdapter = createPageRenderServerAdapter(nav)\n let userAgent: string | undefined\n\n // `renderRoot` calls `initReq` itself with its own overrides (query\n // re-nesting, `urlSuffix`, `fallbackLocale`). Forward them, injecting the\n // page-render `ServerAdapter` so `req.server.redirect()` / `.notFound()`\n // is recorded + thrown rather than escaping as raw TanStack nav.\n const boundInitReq: Parameters<typeof renderRoot>[0]['initReq'] = async (args) => {\n const result = await initReq({\n configPromise: args.configPromise,\n importMap: args.importMap,\n overrides: args.overrides,\n serverAdapter: pageServerAdapter,\n })\n userAgent = result.headers.get('user-agent') ?? undefined\n return result\n }\n\n const notFound = (): never => {\n nav.type = 'notFound'\n throw new Error('not-found')\n }\n const redirect = (url: string): never => {\n nav.type = 'redirect'\n nav.url = url\n throw new Error(`redirect:${url}`)\n }\n\n // Build the 404 result the route loader re-throws as TanStack `notFound()`.\n //\n // Throwing `notFound()` is the only way to set the SSR document status to 404\n // (it's read from `router.stores.statusCode`, set by a not-found match — NOT\n // from `setResponseStatus`, which only affects the RSC RPC response). But the\n // matching `notFoundComponent` is a client component with no access to the\n // Payload `req`, so it can't build the admin chrome on its own. To match Next\n // (whose not-found route renders the full admin layout — nav sidebar, etc. —\n // around the NotFound body, see `renderNotFoundPage`), we render that same\n // shared `renderNotFoundPage` tree here, server-side, and ship its Flight\n // payload through the `notFound()` error so the client renders it verbatim.\n // For users without admin access `renderNotFoundPage` returns the bare\n // `NotFoundClient`, preserving the access-denied behavior.\n const renderNotFound = async (): Promise<LoadAdminPageResult> => {\n const { renderNotFoundPage } = await import('@payloadcms/ui/views/NotFound/page')\n\n const notFoundNode = await renderNotFoundPage({\n config: Promise.resolve(config),\n importMap,\n initReq: (args) =>\n initReq({\n configPromise: args.configPromise,\n importMap: args.importMap,\n overrides: args.overrides,\n }),\n params: Promise.resolve({ segments: splatSegments }),\n searchParams: Promise.resolve(searchParams),\n })\n\n const rscPayload = await renderServerComponent(notFoundNode as React.ReactElement)\n\n return { _notFound: true, routeKey: splat ?? '', rscPayload }\n }\n\n try {\n const node = await renderRoot({\n adminViews: defaultAdminViews,\n config: Promise.resolve(config),\n importMap,\n initReq: boundInitReq,\n notFound,\n // `segments` is intentionally `undefined` for the admin root (`/admin`),\n // matching Next's optional catch-all; `renderRoot` handles it at runtime.\n params: Promise.resolve({ segments }) as Parameters<typeof renderRoot>[0]['params'],\n redirect,\n searchParams: Promise.resolve(searchParams),\n })\n\n const rscPayload = await renderServerComponent(node as React.ReactElement)\n\n // The server-function (client-nav RPC) path returns the flight stream unread —\n // unlike the router/SSR path, which awaits a decode that drives the render to\n // completion. So streamed side effects (e.g. DocumentView's autosave-create\n // `server.redirect()`) haven't run yet and `nav` is empty. Buffer the stream to\n // completion to force the render (populating `nav`), then hand the client a fresh\n // replayable stream from that buffer.\n if (!nav.type) {\n const wrapper = (\n rscPayload as unknown as Record<\n symbol,\n { createReplayStream?: () => ReadableStream<Uint8Array> } | undefined\n >\n )[Symbol.for('tanstack.rsc.stream')]\n if (typeof wrapper?.createReplayStream === 'function') {\n const buffer = await new Response(wrapper.createReplayStream()).arrayBuffer()\n wrapper.createReplayStream = () => new Response(buffer).body as ReadableStream<Uint8Array>\n }\n }\n\n // Navigation thrown deep inside a streamed view component (e.g. access\n // denied → notFound, already-authenticated → redirect) is swallowed into\n // the RSC stream and never rejects the render. Honor it from the holder,\n // discarding the (broken) payload from the aborted render.\n if (nav.type === 'redirect' && nav.url) {\n return { _redirect: nav.url }\n }\n if (nav.type === 'notFound') {\n return await renderNotFound()\n }\n\n // Resolve metadata through the same shared generator Next.js uses. Only\n // plain strings cross the wire (the full `MetaConfig` carries a `URL`\n // `metadataBase` and icons that seroval cannot serialize).\n const i18n = await getRequestI18n({ config })\n const meta = await generatePageMetadata({\n adminViews: defaultAdminViews as Parameters<typeof generatePageMetadata>[0]['adminViews'],\n config,\n i18n,\n params: { segments },\n })\n\n return {\n metadata: {\n ...toAdminPageMetadata(meta),\n viewport: getViewportContent(userAgent),\n },\n routeKey: splat ?? '',\n rscPayload,\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n if (nav.type === 'notFound' || message === 'not-found') {\n return await renderNotFound()\n }\n if (nav.type === 'redirect' || message.startsWith('redirect:')) {\n return { _redirect: nav.url ?? message.slice('redirect:'.length) }\n }\n throw err\n }\n}\n"],"names":["getViewportContent","renderServerComponent","getRequestI18n","initReq","createPageRenderServerAdapter","resolveTitle","title","undefined","absolute","default","toAdminPageMetadata","meta","og","openGraph","rawImages","images","imagesArray","Array","isArray","map","image","url","alt","height","String","width","filter","Boolean","rawIcons","icons","iconList","icon","rel","type","media","sizes","keywords","description","length","join","siteName","robots","loadAdminPage","config","importMap","search","splat","renderRoot","defaultAdminViews","generatePageMetadata","splatSegments","split","segments","searchParams","nav","pageServerAdapter","userAgent","boundInitReq","args","result","configPromise","overrides","serverAdapter","headers","get","notFound","Error","redirect","renderNotFound","renderNotFoundPage","notFoundNode","Promise","resolve","params","rscPayload","_notFound","routeKey","node","adminViews","wrapper","Symbol","for","createReplayStream","buffer","Response","arrayBuffer","body","_redirect","i18n","metadata","viewport","err","message","startsWith","slice"],"mappings":"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"}
|
package/dist/utilities/meta.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../../src/utilities/meta.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../../src/utilities/meta.ts"],"names":[],"mappings":"AAEA,KAAK,SAAS,GACV;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GACnB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAErB,KAAK,SAAS,GAAG;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,KAAK,aAAa,GAAG;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,KAAK,gBAAgB,GAAG;IACtB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,aAAa,EAAE,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE;QACV,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAA;QAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;IACD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,QAAQ,CAAC,EAAE,iBAAiB,GAAG;IAC1D,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,IAAI,EAAE,SAAS,EAAE,CAAA;CAClB,CA0EA"}
|
package/dist/utilities/meta.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getViewportContent } from '@payloadcms/ui/shared';
|
|
1
2
|
/**
|
|
2
3
|
* Builds TanStack Router `head()` `meta` + `links` entries for an admin page
|
|
3
4
|
* from the resolved `AdminPageMetadata`, mirroring the tags Next.js renders
|
|
@@ -16,7 +17,7 @@
|
|
|
16
17
|
},
|
|
17
18
|
{
|
|
18
19
|
name: 'viewport',
|
|
19
|
-
content:
|
|
20
|
+
content: metadata?.viewport ?? getViewportContent()
|
|
20
21
|
}
|
|
21
22
|
];
|
|
22
23
|
const links = [];
|