@payload-onpage/next 0.2.1 → 0.3.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/dist/client.d.ts +12 -12
- package/dist/client.d.ts.map +1 -1
- package/dist/editor.d.ts +2 -4
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +12 -8
- package/dist/editor.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +2 -2
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +2 -6
- package/dist/proxy.js.map +1 -1
- package/dist/session.d.ts +11 -9
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +30 -51
- package/dist/session.js.map +1 -1
- package/package.json +4 -4
- package/src/client.tsx +12 -12
- package/src/editor.tsx +15 -10
- package/src/index.tsx +2 -2
- package/src/proxy.ts +12 -19
- package/src/session.ts +44 -42
package/dist/client.d.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import type { DataSource, Locator, MediaLibrary } from '@payload-onpage/core';
|
|
1
|
+
import type { DataSource, Locator, MediaLibrary, OnPageMode } from '@payload-onpage/core';
|
|
2
2
|
import type { FieldEditorDef, Messages } from '@payload-onpage/react';
|
|
3
3
|
export interface OnPageMountProps {
|
|
4
|
-
mode:
|
|
4
|
+
mode: OnPageMode;
|
|
5
5
|
/** Opened from a shared preview link (no editing). */
|
|
6
|
-
shared?: boolean;
|
|
6
|
+
shared?: boolean | undefined;
|
|
7
7
|
/** Payload API route. Default "/api". */
|
|
8
|
-
apiRoute?: string;
|
|
8
|
+
apiRoute?: string | undefined;
|
|
9
9
|
/** Plugin endpoint prefix. Default "/onpage". */
|
|
10
|
-
prefix?: string;
|
|
10
|
+
prefix?: `/${string}` | undefined;
|
|
11
11
|
/** Payload admin route. Default "/admin". */
|
|
12
|
-
adminRoute?: string;
|
|
12
|
+
adminRoute?: string | undefined;
|
|
13
13
|
/** Locale to edit. Default: <html lang>. */
|
|
14
|
-
locale?: string;
|
|
15
|
-
editors?: Record<string, FieldEditorDef | null
|
|
16
|
-
locator?: Locator;
|
|
17
|
-
media?: MediaLibrary;
|
|
18
|
-
dataSource?: DataSource;
|
|
14
|
+
locale?: string | undefined;
|
|
15
|
+
editors?: Record<string, FieldEditorDef | null> | undefined;
|
|
16
|
+
locator?: Locator | undefined;
|
|
17
|
+
media?: MediaLibrary | undefined;
|
|
18
|
+
dataSource?: DataSource | undefined;
|
|
19
19
|
/** Override editor UI strings (see `defaultMessages` in @payload-onpage/react). */
|
|
20
|
-
messages?: Messages;
|
|
20
|
+
messages?: Messages | undefined;
|
|
21
21
|
}
|
|
22
22
|
export declare function OnPageMount(props: OnPageMountProps): import("react").JSX.Element;
|
|
23
23
|
export { OnPageClient } from '@payload-onpage/react/client';
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACzF,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAErE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,UAAU,CAAA;IAChB,sDAAsD;IACtD,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC5B,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,iDAAiD;IACjD,MAAM,CAAC,EAAE,IAAI,MAAM,EAAE,GAAG,SAAS,CAAA;IACjC,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAE3B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC,GAAG,SAAS,CAAA;IAC3D,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,KAAK,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;IAChC,UAAU,CAAC,EAAE,UAAU,GAAG,SAAS,CAAA;IACnC,mFAAmF;IACnF,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;CAChC;AAKD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BAElD;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/editor.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
/** The lazily loaded part of the Next adapter: the editor and the preview toolbar, wired to Next and Payload. */
|
|
2
|
-
import React from 'react';
|
|
3
1
|
import type { OnPageMountProps } from './client.tsx';
|
|
4
|
-
export declare function NextEditor(props: OnPageMountProps):
|
|
5
|
-
export declare function NextPreview(props: OnPageMountProps):
|
|
2
|
+
export declare function NextEditor(props: OnPageMountProps): import("react").JSX.Element | null;
|
|
3
|
+
export declare function NextPreview(props: OnPageMountProps): import("react").JSX.Element;
|
|
6
4
|
//# sourceMappingURL=editor.d.ts.map
|
package/dist/editor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAoCpD,wBAAgB,UAAU,CAAC,KAAK,EAAE,gBAAgB,sCAwBjD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BAUlD"}
|
package/dist/editor.js
CHANGED
|
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
/** The lazily loaded part of the Next adapter: the editor and the preview toolbar, wired to Next and Payload. */
|
|
4
4
|
import { useMemo } from 'react';
|
|
5
5
|
import { usePathname, useSearchParams } from 'next/navigation.js';
|
|
6
|
-
import { DEFAULTS } from '@payload-onpage/core';
|
|
6
|
+
import { DEFAULTS, ROUTES } from '@payload-onpage/core';
|
|
7
7
|
import { payloadRest, payloadUploads } from '@payload-onpage/core/payload';
|
|
8
8
|
import { OnPageEditor, PreviewToolbar, inPreviewFrame } from '@payload-onpage/react';
|
|
9
9
|
import { lexicalEditors } from '@payload-onpage/lexical';
|
|
@@ -14,14 +14,18 @@ function useHere() {
|
|
|
14
14
|
const qs = search?.toString();
|
|
15
15
|
return `${pathname}${qs ? `?${qs}` : ''}`;
|
|
16
16
|
}
|
|
17
|
+
/** Routes resolved once, with the defaults applied. */
|
|
18
|
+
const routesOf = (props) => {
|
|
19
|
+
const api = props.apiRoute ?? DEFAULTS.apiRoute;
|
|
20
|
+
const prefix = props.prefix ?? DEFAULTS.routePrefix;
|
|
21
|
+
return { api, prefix, base: `${api}${prefix}`, admin: props.adminRoute ?? DEFAULTS.adminRoute };
|
|
22
|
+
};
|
|
17
23
|
function useLinks(props) {
|
|
18
24
|
const here = useHere();
|
|
19
|
-
const
|
|
20
|
-
const base = `${api}${props.prefix ?? DEFAULTS.routePrefix}`;
|
|
21
|
-
const admin = props.adminRoute ?? DEFAULTS.adminRoute;
|
|
25
|
+
const { base, admin } = routesOf(props);
|
|
22
26
|
return useMemo(() => ({
|
|
23
|
-
exit: `${base}
|
|
24
|
-
mode: (mode) => `${base}
|
|
27
|
+
exit: `${base}${ROUTES.exit}?path=${encodeURIComponent(here)}`,
|
|
28
|
+
mode: (mode) => `${base}${ROUTES.mode}?mode=${mode}&path=${encodeURIComponent(here)}`,
|
|
25
29
|
admin: (ref) => ref.kind === 'global'
|
|
26
30
|
? `${admin}/globals/${ref.slug}`
|
|
27
31
|
: `${admin}/collections/${ref.slug}/${ref.id}`,
|
|
@@ -32,8 +36,8 @@ export function NextEditor(props) {
|
|
|
32
36
|
const locale = props.locale ?? htmlLang();
|
|
33
37
|
const links = useLinks(props);
|
|
34
38
|
const renderer = useNextRefresh();
|
|
35
|
-
const api = props
|
|
36
|
-
const dataSource = useMemo(() => props.dataSource ?? payloadRest({ apiBase: api, prefix
|
|
39
|
+
const { api, prefix } = routesOf(props);
|
|
40
|
+
const dataSource = useMemo(() => props.dataSource ?? payloadRest({ apiBase: api, prefix }), [props.dataSource, api, prefix]);
|
|
37
41
|
const media = useMemo(() => props.media ?? payloadUploads({ apiBase: api }), [props.media, api]);
|
|
38
42
|
const editors = useMemo(() => ({ ...lexicalEditors, ...props.editors }), [props.editors]);
|
|
39
43
|
if (inPreviewFrame())
|
package/dist/editor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,iHAAiH;AACjH,
|
|
1
|
+
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -7,9 +7,9 @@ export * from "./session.js";
|
|
|
7
7
|
* renders nothing otherwise. Static-safe: it reads cookies only in draft mode.
|
|
8
8
|
*/
|
|
9
9
|
export async function OnPage({ mount: Mount = OnPageMount, ...props }) {
|
|
10
|
-
const mode = await getOnPageMode();
|
|
10
|
+
const [mode, shared] = await Promise.all([getOnPageMode(), isSharedPreview()]);
|
|
11
11
|
if (!mode)
|
|
12
12
|
return null;
|
|
13
|
-
return _jsx(Mount, { ...props, mode: mode, shared:
|
|
13
|
+
return _jsx(Mount, { ...props, mode: mode, shared: shared });
|
|
14
14
|
}
|
|
15
15
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":";AAcA,OAAO,EAAE,WAAW,EAAyB,MAAM,aAAc,CAAA;AACjE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE7D,cAAc,cAAc,CAAA;AAgB5B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,WAAW,EAAE,GAAG,KAAK,EAAe;IAChF,MAAM,IAAI,GAAG,MAAM,aAAa,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":";AAcA,OAAO,EAAE,WAAW,EAAyB,MAAM,aAAc,CAAA;AACjE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE7D,cAAc,cAAc,CAAA;AAgB5B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,WAAW,EAAE,GAAG,KAAK,EAAe;IAChF,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,aAAa,EAAE,EAAE,eAAe,EAAE,CAAC,CAAC,CAAA;IAC9E,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IACtB,OAAO,KAAC,KAAK,OAAK,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAI,CAAA;AACzD,CAAC"}
|
package/dist/proxy.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type NextMiddleware, type NextRequest } from 'next/server.js';
|
|
2
2
|
export interface OnPageProxyOptions {
|
|
3
3
|
/** Same secret as the plugin (default: Payload's `secret`, i.e. PAYLOAD_SECRET). */
|
|
4
4
|
secret: string | undefined;
|
|
5
5
|
}
|
|
6
6
|
/** Request headers for the rest of the pipeline, with draft mode injected for a valid grant. */
|
|
7
7
|
export declare function withOnPage(request: NextRequest, opts: OnPageProxyOptions, headers?: Headers): Promise<Headers>;
|
|
8
|
-
export declare function createOnPageProxy(opts: OnPageProxyOptions):
|
|
8
|
+
export declare function createOnPageProxy(opts: OnPageProxyOptions): NextMiddleware;
|
|
9
9
|
//# sourceMappingURL=proxy.d.ts.map
|
package/dist/proxy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAgB,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEpF,MAAM,WAAW,kBAAkB;IACjC,oFAAoF;IACpF,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;CAC3B;AAQD,gGAAgG;AAChG,wBAAsB,UAAU,CAC9B,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,kBAAkB,EACxB,OAAO,GAAE,OAAsC,GAC9C,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,cAAc,CAQ1E"}
|
package/dist/proxy.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* Already have one? `NextResponse.next({ request: { headers: await withOnPage(request, { secret }) } })`
|
|
15
15
|
*/
|
|
16
|
-
import { COOKIE, HEADER, SEAL_PURPOSE, samePath, unseal } from '@payload-onpage/core';
|
|
16
|
+
import { COOKIE, HEADER, SEAL_PURPOSE, isLivePreviewGrant, samePath, unseal, } from '@payload-onpage/core';
|
|
17
17
|
import { NextResponse } from 'next/server.js';
|
|
18
18
|
const withoutCookies = (header, names) => (header ?? '')
|
|
19
19
|
.split(';')
|
|
@@ -28,12 +28,8 @@ export async function withOnPage(request, opts, headers = new Headers(request.he
|
|
|
28
28
|
if (!sealed || !opts.secret)
|
|
29
29
|
return headers;
|
|
30
30
|
const grant = await unseal(sealed, opts.secret, SEAL_PURPOSE.previewCookie);
|
|
31
|
-
if (!grant ||
|
|
32
|
-
typeof grant.draft !== 'string' ||
|
|
33
|
-
grant.exp * 1000 <= Date.now() ||
|
|
34
|
-
!samePath(path, grant.path)) {
|
|
31
|
+
if (!isLivePreviewGrant(grant) || !samePath(path, grant.path))
|
|
35
32
|
return headers;
|
|
36
|
-
}
|
|
37
33
|
const cookie = withoutCookies(headers.get('cookie'), [COOKIE.nextDraft, COOKIE.mode]);
|
|
38
34
|
cookie.push(`${COOKIE.nextDraft}=${grant.draft}`, `${COOKIE.mode}=view`);
|
|
39
35
|
headers.set('cookie', cookie.join('; '));
|
package/dist/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EACL,MAAM,EACN,MAAM,EACN,YAAY,EACZ,kBAAkB,EAClB,QAAQ,EACR,MAAM,GACP,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,YAAY,EAAyC,MAAM,gBAAgB,CAAA;AAOpF,MAAM,cAAc,GAAG,CAAC,MAAqB,EAAE,KAAe,EAAE,EAAE,CAChE,CAAC,MAAM,IAAI,EAAE,CAAC;KACX,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;KACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AAElE,gGAAgG;AAChG,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAoB,EACpB,IAAwB,EACxB,UAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;IAE/C,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAA;IACrC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA,CAAC,iCAAiC;IAC/D,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,KAAK,CAAA;IACzD,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO,OAAO,CAAA;IAC3C,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,aAAa,CAAC,CAAA;IAC3E,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,OAAO,CAAA;IAC7E,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IACrF,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,MAAM,CAAC,IAAI,OAAO,CAAC,CAAA;IACxE,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IACxC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC/B,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAwB;IACxD,IAAI,CAAC,IAAI,CAAC,MAAM;QACd,OAAO,CAAC,IAAI,CACV,+FAA+F,CAChG,CAAA;IACH,OAAO,KAAK,UAAU,KAAK,CAAC,OAAoB;QAC9C,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAA;IACrF,CAAC,CAAA;AACH,CAAC"}
|
package/dist/session.d.ts
CHANGED
|
@@ -12,31 +12,33 @@
|
|
|
12
12
|
* draft mode is on. Next exempts that read from prerender bailout, so pages
|
|
13
13
|
* stay static for visitors.
|
|
14
14
|
*/
|
|
15
|
-
import { type OnPageMode, type SessionAdapter } from '@payload-onpage/core';
|
|
15
|
+
import { CONTEXT_KEY, type OnPageMode, type SessionAdapter } from '@payload-onpage/core';
|
|
16
16
|
export interface NextSessionOptions {
|
|
17
17
|
/** How long an editor's edit/preview mode lasts, in seconds. Default 12 hours. */
|
|
18
18
|
maxAge?: number;
|
|
19
19
|
}
|
|
20
20
|
export declare function nextSession(opts?: NextSessionOptions): SessionAdapter;
|
|
21
21
|
/** "edit", "view" or null for this request. Safe to call anywhere (returns null outside a request). */
|
|
22
|
-
export declare
|
|
22
|
+
export declare const getOnPageMode: () => Promise<OnPageMode | null>;
|
|
23
23
|
/**
|
|
24
24
|
* Should this request render drafts? `draftMode().isEnabled`, except that an
|
|
25
25
|
* editor whose session ended (signed out) no longer sees drafts. Draft mode
|
|
26
26
|
* turned on by something else (your own preview route) is respected.
|
|
27
27
|
*/
|
|
28
|
-
export declare
|
|
28
|
+
export declare const isDraftEnabled: () => Promise<boolean>;
|
|
29
29
|
/** Is this request a preview-link view (set by the proxy)? */
|
|
30
|
-
export declare
|
|
30
|
+
export declare const isSharedPreview: () => Promise<boolean>;
|
|
31
|
+
/** What `onpageContext()` adds to a Payload read in edit mode. */
|
|
32
|
+
export type OnPageReadContext = {
|
|
33
|
+
context?: {
|
|
34
|
+
[CONTEXT_KEY]: true;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
31
37
|
/**
|
|
32
38
|
* Spread into Payload reads that feed the page: in edit mode it turns on
|
|
33
39
|
* auto-mapping markers, otherwise it is empty.
|
|
34
40
|
*
|
|
35
41
|
* payload.find({ collection: 'pages', where, draft, ...(await onpageContext()) })
|
|
36
42
|
*/
|
|
37
|
-
export declare function onpageContext(): Promise<
|
|
38
|
-
context?: {
|
|
39
|
-
onpage: true;
|
|
40
|
-
};
|
|
41
|
-
}>;
|
|
43
|
+
export declare function onpageContext(): Promise<OnPageReadContext>;
|
|
42
44
|
//# sourceMappingURL=session.d.ts.map
|
package/dist/session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,WAAW,EAKX,KAAK,UAAU,EACf,KAAK,cAAc,EACpB,MAAM,sBAAsB,CAAA;AAG7B,MAAM,WAAW,kBAAkB;IACjC,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AA0CD,wBAAgB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG,cAAc,CA2BzE;AAED,uGAAuG;AACvG,eAAO,MAAM,aAAa,QAAO,OAAO,CAAC,UAAU,GAAG,IAAI,CAIhD,CAAA;AAEV;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAO,OAAO,CAAC,OAAO,CAKtC,CAAA;AAEX,8DAA8D;AAC9D,eAAO,MAAM,eAAe,QAAO,OAAO,CAAC,OAAO,CACwB,CAAA;AAE1E,kEAAkE;AAClE,MAAM,MAAM,iBAAiB,GAAG;IAAE,OAAO,CAAC,EAAE;QAAE,CAAC,WAAW,CAAC,EAAE,IAAI,CAAA;KAAE,CAAA;CAAE,CAAA;AAErE;;;;;GAKG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAEhE"}
|
package/dist/session.js
CHANGED
|
@@ -12,21 +12,35 @@
|
|
|
12
12
|
* draft mode is on. Next exempts that read from prerender bailout, so pages
|
|
13
13
|
* stay static for visitors.
|
|
14
14
|
*/
|
|
15
|
-
import { COOKIE, HEADER, sha256 } from '@payload-onpage/core';
|
|
15
|
+
import { CONTEXT_KEY, COOKIE, HEADER, isOnPageMode, sha256, } from '@payload-onpage/core';
|
|
16
16
|
import { cookies, draftMode, headers } from 'next/headers.js';
|
|
17
|
+
/** Our cookies: server-only, same-site, whole site. */
|
|
18
|
+
const cookieBase = (secure) => ({ httpOnly: true, sameSite: 'lax', secure, path: '/' });
|
|
19
|
+
/**
|
|
20
|
+
* Runs `read` only in draft mode, so visitors' requests never touch cookies and
|
|
21
|
+
* pages stay static. Outside a request (build, scripts) it returns `fallback`.
|
|
22
|
+
*/
|
|
23
|
+
async function inDraft(read, fallback) {
|
|
24
|
+
try {
|
|
25
|
+
return (await draftMode()).isEnabled ? await read() : fallback;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return fallback;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
17
31
|
const HASH_LENGTH = 32;
|
|
18
32
|
const fingerprint = async (token) => (await sha256(token)).slice(0, HASH_LENGTH);
|
|
19
33
|
/** `edit` | `view`, or `<mode>:<authCookie>:<hash>` when bound to a session. */
|
|
20
34
|
async function modeValue(mode, jar, bindTo) {
|
|
21
35
|
const token = bindTo ? jar.get(bindTo)?.value : undefined;
|
|
22
|
-
return token
|
|
36
|
+
return token ? `${mode}:${bindTo}:${await fingerprint(token)}` : mode;
|
|
23
37
|
}
|
|
24
38
|
/** The mode a cookie value grants in this request, or null (unknown, or the bound session is gone). */
|
|
25
39
|
async function readMode(raw, jar) {
|
|
26
40
|
if (!raw)
|
|
27
41
|
return null;
|
|
28
42
|
const [mode, bound, hash] = raw.split(':');
|
|
29
|
-
if (mode
|
|
43
|
+
if (!isOnPageMode(mode))
|
|
30
44
|
return null;
|
|
31
45
|
if (bound) {
|
|
32
46
|
const token = jar.get(bound)?.value;
|
|
@@ -42,13 +56,7 @@ export function nextSession(opts = {}) {
|
|
|
42
56
|
;
|
|
43
57
|
(await draftMode()).enable();
|
|
44
58
|
const jar = await cookies();
|
|
45
|
-
jar.set(COOKIE.mode, await modeValue(mode, jar, bindTo), {
|
|
46
|
-
httpOnly: true,
|
|
47
|
-
sameSite: 'lax',
|
|
48
|
-
secure,
|
|
49
|
-
path: '/',
|
|
50
|
-
maxAge,
|
|
51
|
-
});
|
|
59
|
+
jar.set(COOKIE.mode, await modeValue(mode, jar, bindTo), { ...cookieBase(secure), maxAge });
|
|
52
60
|
jar.delete(COOKIE.preview);
|
|
53
61
|
},
|
|
54
62
|
async enableLink({ secure, path: _path, expiresAt, seal }) {
|
|
@@ -61,13 +69,7 @@ export function nextSession(opts = {}) {
|
|
|
61
69
|
// Keep the draft secret server-side: the browser only gets the sealed grant.
|
|
62
70
|
jar.delete(COOKIE.nextDraft);
|
|
63
71
|
jar.delete(COOKIE.mode);
|
|
64
|
-
jar.set(COOKIE.preview, await seal(draft), {
|
|
65
|
-
httpOnly: true,
|
|
66
|
-
sameSite: 'lax',
|
|
67
|
-
secure,
|
|
68
|
-
path: '/',
|
|
69
|
-
expires: expiresAt,
|
|
70
|
-
});
|
|
72
|
+
jar.set(COOKIE.preview, await seal(draft), { ...cookieBase(secure), expires: expiresAt });
|
|
71
73
|
},
|
|
72
74
|
async disable() {
|
|
73
75
|
;
|
|
@@ -80,45 +82,22 @@ export function nextSession(opts = {}) {
|
|
|
80
82
|
};
|
|
81
83
|
}
|
|
82
84
|
/** "edit", "view" or null for this request. Safe to call anywhere (returns null outside a request). */
|
|
83
|
-
export
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const jar = await cookies();
|
|
88
|
-
return await readMode(jar.get(COOKIE.mode)?.value, jar);
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
85
|
+
export const getOnPageMode = () => inDraft(async () => {
|
|
86
|
+
const jar = await cookies();
|
|
87
|
+
return readMode(jar.get(COOKIE.mode)?.value, jar);
|
|
88
|
+
}, null);
|
|
94
89
|
/**
|
|
95
90
|
* Should this request render drafts? `draftMode().isEnabled`, except that an
|
|
96
91
|
* editor whose session ended (signed out) no longer sees drafts. Draft mode
|
|
97
92
|
* turned on by something else (your own preview route) is respected.
|
|
98
93
|
*/
|
|
99
|
-
export
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const raw = jar.get(COOKIE.mode)?.value;
|
|
105
|
-
return raw === undefined || (await readMode(raw, jar)) !== null;
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
94
|
+
export const isDraftEnabled = () => inDraft(async () => {
|
|
95
|
+
const jar = await cookies();
|
|
96
|
+
const raw = jar.get(COOKIE.mode)?.value;
|
|
97
|
+
return raw === undefined || (await readMode(raw, jar)) !== null;
|
|
98
|
+
}, false);
|
|
111
99
|
/** Is this request a preview-link view (set by the proxy)? */
|
|
112
|
-
export async
|
|
113
|
-
try {
|
|
114
|
-
if (!(await draftMode()).isEnabled)
|
|
115
|
-
return false;
|
|
116
|
-
return (await headers()).get(HEADER.shared) === '1';
|
|
117
|
-
}
|
|
118
|
-
catch {
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
100
|
+
export const isSharedPreview = () => inDraft(async () => (await headers()).get(HEADER.shared) === '1', false);
|
|
122
101
|
/**
|
|
123
102
|
* Spread into Payload reads that feed the page: in edit mode it turns on
|
|
124
103
|
* auto-mapping markers, otherwise it is empty.
|
|
@@ -126,6 +105,6 @@ export async function isSharedPreview() {
|
|
|
126
105
|
* payload.find({ collection: 'pages', where, draft, ...(await onpageContext()) })
|
|
127
106
|
*/
|
|
128
107
|
export async function onpageContext() {
|
|
129
|
-
return (await getOnPageMode()) === 'edit' ? { context: {
|
|
108
|
+
return (await getOnPageMode()) === 'edit' ? { context: { [CONTEXT_KEY]: true } } : {};
|
|
130
109
|
}
|
|
131
110
|
//# sourceMappingURL=session.js.map
|
package/dist/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,WAAW,EACX,MAAM,EACN,MAAM,EACN,YAAY,EACZ,MAAM,GAGP,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAU7D,uDAAuD;AACvD,MAAM,UAAU,GAAG,CAAC,MAAe,EAAE,EAAE,CACrC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAyB,CAAA;AAElF;;;GAGG;AACH,KAAK,UAAU,OAAO,CAAI,IAAsB,EAAE,QAAW;IAC3D,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAA;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAA;IACjB,CAAC;AACH,CAAC;AAED,MAAM,WAAW,GAAG,EAAE,CAAA;AACtB,MAAM,WAAW,GAAG,KAAK,EAAE,KAAa,EAAE,EAAE,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAA;AAExF,gFAAgF;AAChF,KAAK,UAAU,SAAS,CAAC,IAAgB,EAAE,GAAQ,EAAE,MAAe;IAClE,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;IACzD,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,MAAM,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AACvE,CAAC;AAED,uGAAuG;AACvG,KAAK,UAAU,QAAQ,CAAC,GAAuB,EAAE,GAAQ;IACvD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC1C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IACpC,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,KAAK,CAAA;QACnC,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI;YAAE,OAAO,IAAI,CAAA;IAChE,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAA2B,EAAE;IACvD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,GAAG,IAAI,CAAA;IACvC,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;YACnC,CAAC;YAAA,CAAC,MAAM,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAA;YAC7B,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAA;YAC3B,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAA;YAC3F,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAC5B,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE;YACvD,CAAC;YAAA,CAAC,MAAM,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAA;YAC7B,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAA;YAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,KAAK,CAAA;YAC9C,IAAI,CAAC,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAA;YACxF,6EAA6E;YAC7E,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;YAC5B,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YACvB,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAA;QAC3F,CAAC;QACD,KAAK,CAAC,OAAO;YACX,CAAC;YAAA,CAAC,MAAM,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,CAAA;YAC9B,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAA;YAC3B,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YACvB,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAC5B,CAAC;QACD,OAAO,EAAE,aAAa;KACvB,CAAA;AACH,CAAC;AAED,uGAAuG;AACvG,MAAM,CAAC,MAAM,aAAa,GAAG,GAA+B,EAAE,CAC5D,OAAO,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAA;IAC3B,OAAO,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,CAAA;AACnD,CAAC,EAAE,IAAI,CAAC,CAAA;AAEV;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAqB,EAAE,CACnD,OAAO,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAA;IAC3B,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,CAAA;IACvC,OAAO,GAAG,KAAK,SAAS,IAAI,CAAC,MAAM,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,IAAI,CAAA;AACjE,CAAC,EAAE,KAAK,CAAC,CAAA;AAEX,8DAA8D;AAC9D,MAAM,CAAC,MAAM,eAAe,GAAG,GAAqB,EAAE,CACpD,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,KAAK,CAAC,CAAA;AAK1E;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,OAAO,CAAC,MAAM,aAAa,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;AACvF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payload-onpage/next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "payload-onpage for Next.js App Router: draft-mode session adapter, <OnPage />, onpageContext() and refresh rendering.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -61,9 +61,9 @@
|
|
|
61
61
|
"provenance": true
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@payload-onpage/core": "0.
|
|
65
|
-
"@payload-onpage/react": "0.
|
|
66
|
-
"@payload-onpage/lexical": "0.
|
|
64
|
+
"@payload-onpage/core": "0.3.0",
|
|
65
|
+
"@payload-onpage/react": "0.3.0",
|
|
66
|
+
"@payload-onpage/lexical": "0.3.0"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
69
|
"next": "^15.4.11 || ^16.3.8",
|
package/src/client.tsx
CHANGED
|
@@ -5,28 +5,28 @@
|
|
|
5
5
|
* is loaded only when someone is editing or previewing.
|
|
6
6
|
*/
|
|
7
7
|
import dynamic from 'next/dynamic.js'
|
|
8
|
-
import type { DataSource, Locator, MediaLibrary } from '@payload-onpage/core'
|
|
8
|
+
import type { DataSource, Locator, MediaLibrary, OnPageMode } from '@payload-onpage/core'
|
|
9
9
|
import type { FieldEditorDef, Messages } from '@payload-onpage/react'
|
|
10
10
|
|
|
11
11
|
export interface OnPageMountProps {
|
|
12
|
-
mode:
|
|
12
|
+
mode: OnPageMode
|
|
13
13
|
/** Opened from a shared preview link (no editing). */
|
|
14
|
-
shared?: boolean
|
|
14
|
+
shared?: boolean | undefined
|
|
15
15
|
/** Payload API route. Default "/api". */
|
|
16
|
-
apiRoute?: string
|
|
16
|
+
apiRoute?: string | undefined
|
|
17
17
|
/** Plugin endpoint prefix. Default "/onpage". */
|
|
18
|
-
prefix?: string
|
|
18
|
+
prefix?: `/${string}` | undefined
|
|
19
19
|
/** Payload admin route. Default "/admin". */
|
|
20
|
-
adminRoute?: string
|
|
20
|
+
adminRoute?: string | undefined
|
|
21
21
|
/** Locale to edit. Default: <html lang>. */
|
|
22
|
-
locale?: string
|
|
22
|
+
locale?: string | undefined
|
|
23
23
|
// Client-only options (pass them from your own 'use client' mount):
|
|
24
|
-
editors?: Record<string, FieldEditorDef | null>
|
|
25
|
-
locator?: Locator
|
|
26
|
-
media?: MediaLibrary
|
|
27
|
-
dataSource?: DataSource
|
|
24
|
+
editors?: Record<string, FieldEditorDef | null> | undefined
|
|
25
|
+
locator?: Locator | undefined
|
|
26
|
+
media?: MediaLibrary | undefined
|
|
27
|
+
dataSource?: DataSource | undefined
|
|
28
28
|
/** Override editor UI strings (see `defaultMessages` in @payload-onpage/react). */
|
|
29
|
-
messages?: Messages
|
|
29
|
+
messages?: Messages | undefined
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const Editor = dynamic(() => import('./editor.tsx').then((m) => m.NextEditor), { ssr: false })
|
package/src/editor.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
/** The lazily loaded part of the Next adapter: the editor and the preview toolbar, wired to Next and Payload. */
|
|
4
|
-
import
|
|
4
|
+
import { useMemo } from 'react'
|
|
5
5
|
import { usePathname, useSearchParams } from 'next/navigation.js'
|
|
6
|
-
import { DEFAULTS, type DocRef } from '@payload-onpage/core'
|
|
6
|
+
import { DEFAULTS, ROUTES, type DocRef } from '@payload-onpage/core'
|
|
7
7
|
import { payloadRest, payloadUploads } from '@payload-onpage/core/payload'
|
|
8
8
|
import { OnPageEditor, PreviewToolbar, inPreviewFrame, type Links } from '@payload-onpage/react'
|
|
9
9
|
import { lexicalEditors } from '@payload-onpage/lexical'
|
|
@@ -17,15 +17,20 @@ function useHere(): string {
|
|
|
17
17
|
return `${pathname}${qs ? `?${qs}` : ''}`
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/** Routes resolved once, with the defaults applied. */
|
|
21
|
+
const routesOf = (props: OnPageMountProps) => {
|
|
22
|
+
const api = props.apiRoute ?? DEFAULTS.apiRoute
|
|
23
|
+
const prefix = props.prefix ?? DEFAULTS.routePrefix
|
|
24
|
+
return { api, prefix, base: `${api}${prefix}`, admin: props.adminRoute ?? DEFAULTS.adminRoute }
|
|
25
|
+
}
|
|
26
|
+
|
|
20
27
|
function useLinks(props: OnPageMountProps): Links {
|
|
21
28
|
const here = useHere()
|
|
22
|
-
const
|
|
23
|
-
const base = `${api}${props.prefix ?? DEFAULTS.routePrefix}`
|
|
24
|
-
const admin = props.adminRoute ?? DEFAULTS.adminRoute
|
|
29
|
+
const { base, admin } = routesOf(props)
|
|
25
30
|
return useMemo<Links>(
|
|
26
31
|
() => ({
|
|
27
|
-
exit: `${base}
|
|
28
|
-
mode: (mode) => `${base}
|
|
32
|
+
exit: `${base}${ROUTES.exit}?path=${encodeURIComponent(here)}`,
|
|
33
|
+
mode: (mode) => `${base}${ROUTES.mode}?mode=${mode}&path=${encodeURIComponent(here)}`,
|
|
29
34
|
admin: (ref: DocRef) =>
|
|
30
35
|
ref.kind === 'global'
|
|
31
36
|
? `${admin}/globals/${ref.slug}`
|
|
@@ -42,10 +47,10 @@ export function NextEditor(props: OnPageMountProps) {
|
|
|
42
47
|
const locale = props.locale ?? htmlLang()
|
|
43
48
|
const links = useLinks(props)
|
|
44
49
|
const renderer = useNextRefresh()
|
|
45
|
-
const api = props
|
|
50
|
+
const { api, prefix } = routesOf(props)
|
|
46
51
|
const dataSource = useMemo(
|
|
47
|
-
() => props.dataSource ?? payloadRest({ apiBase: api, prefix
|
|
48
|
-
[props.dataSource, api,
|
|
52
|
+
() => props.dataSource ?? payloadRest({ apiBase: api, prefix }),
|
|
53
|
+
[props.dataSource, api, prefix],
|
|
49
54
|
)
|
|
50
55
|
const media = useMemo(() => props.media ?? payloadUploads({ apiBase: api }), [props.media, api])
|
|
51
56
|
const editors = useMemo(() => ({ ...lexicalEditors, ...props.editors }), [props.editors])
|
package/src/index.tsx
CHANGED
|
@@ -36,7 +36,7 @@ export interface OnPageProps extends Omit<OnPageMountProps, ClientOnly> {
|
|
|
36
36
|
* renders nothing otherwise. Static-safe: it reads cookies only in draft mode.
|
|
37
37
|
*/
|
|
38
38
|
export async function OnPage({ mount: Mount = OnPageMount, ...props }: OnPageProps) {
|
|
39
|
-
const mode = await getOnPageMode()
|
|
39
|
+
const [mode, shared] = await Promise.all([getOnPageMode(), isSharedPreview()])
|
|
40
40
|
if (!mode) return null
|
|
41
|
-
return <Mount {...props} mode={mode} shared={
|
|
41
|
+
return <Mount {...props} mode={mode} shared={shared} />
|
|
42
42
|
}
|
package/src/proxy.ts
CHANGED
|
@@ -13,21 +13,21 @@
|
|
|
13
13
|
*
|
|
14
14
|
* Already have one? `NextResponse.next({ request: { headers: await withOnPage(request, { secret }) } })`
|
|
15
15
|
*/
|
|
16
|
-
import {
|
|
17
|
-
|
|
16
|
+
import {
|
|
17
|
+
COOKIE,
|
|
18
|
+
HEADER,
|
|
19
|
+
SEAL_PURPOSE,
|
|
20
|
+
isLivePreviewGrant,
|
|
21
|
+
samePath,
|
|
22
|
+
unseal,
|
|
23
|
+
} from '@payload-onpage/core'
|
|
24
|
+
import { NextResponse, type NextMiddleware, type NextRequest } from 'next/server.js'
|
|
18
25
|
|
|
19
26
|
export interface OnPageProxyOptions {
|
|
20
27
|
/** Same secret as the plugin (default: Payload's `secret`, i.e. PAYLOAD_SECRET). */
|
|
21
28
|
secret: string | undefined
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
interface PreviewGrant {
|
|
25
|
-
path: string
|
|
26
|
-
ref: string
|
|
27
|
-
exp: number
|
|
28
|
-
draft: string
|
|
29
|
-
}
|
|
30
|
-
|
|
31
31
|
const withoutCookies = (header: string | null, names: string[]) =>
|
|
32
32
|
(header ?? '')
|
|
33
33
|
.split(';')
|
|
@@ -45,15 +45,8 @@ export async function withOnPage(
|
|
|
45
45
|
headers.set(HEADER.path, path)
|
|
46
46
|
const sealed = request.cookies.get(COOKIE.preview)?.value
|
|
47
47
|
if (!sealed || !opts.secret) return headers
|
|
48
|
-
const grant = await unseal
|
|
49
|
-
if (
|
|
50
|
-
!grant ||
|
|
51
|
-
typeof grant.draft !== 'string' ||
|
|
52
|
-
grant.exp * 1000 <= Date.now() ||
|
|
53
|
-
!samePath(path, grant.path)
|
|
54
|
-
) {
|
|
55
|
-
return headers
|
|
56
|
-
}
|
|
48
|
+
const grant = await unseal(sealed, opts.secret, SEAL_PURPOSE.previewCookie)
|
|
49
|
+
if (!isLivePreviewGrant(grant) || !samePath(path, grant.path)) return headers
|
|
57
50
|
const cookie = withoutCookies(headers.get('cookie'), [COOKIE.nextDraft, COOKIE.mode])
|
|
58
51
|
cookie.push(`${COOKIE.nextDraft}=${grant.draft}`, `${COOKIE.mode}=view`)
|
|
59
52
|
headers.set('cookie', cookie.join('; '))
|
|
@@ -61,7 +54,7 @@ export async function withOnPage(
|
|
|
61
54
|
return headers
|
|
62
55
|
}
|
|
63
56
|
|
|
64
|
-
export function createOnPageProxy(opts: OnPageProxyOptions) {
|
|
57
|
+
export function createOnPageProxy(opts: OnPageProxyOptions): NextMiddleware {
|
|
65
58
|
if (!opts.secret)
|
|
66
59
|
console.warn(
|
|
67
60
|
'payload-onpage: createOnPageProxy() has no secret; preview links will show published content.',
|
package/src/session.ts
CHANGED
|
@@ -12,7 +12,15 @@
|
|
|
12
12
|
* draft mode is on. Next exempts that read from prerender bailout, so pages
|
|
13
13
|
* stay static for visitors.
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
CONTEXT_KEY,
|
|
17
|
+
COOKIE,
|
|
18
|
+
HEADER,
|
|
19
|
+
isOnPageMode,
|
|
20
|
+
sha256,
|
|
21
|
+
type OnPageMode,
|
|
22
|
+
type SessionAdapter,
|
|
23
|
+
} from '@payload-onpage/core'
|
|
16
24
|
import { cookies, draftMode, headers } from 'next/headers.js'
|
|
17
25
|
|
|
18
26
|
export interface NextSessionOptions {
|
|
@@ -21,6 +29,23 @@ export interface NextSessionOptions {
|
|
|
21
29
|
}
|
|
22
30
|
|
|
23
31
|
type Jar = Awaited<ReturnType<typeof cookies>>
|
|
32
|
+
type CookieOptions = NonNullable<Parameters<Jar['set']>[2]>
|
|
33
|
+
|
|
34
|
+
/** Our cookies: server-only, same-site, whole site. */
|
|
35
|
+
const cookieBase = (secure: boolean) =>
|
|
36
|
+
({ httpOnly: true, sameSite: 'lax', secure, path: '/' }) satisfies CookieOptions
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Runs `read` only in draft mode, so visitors' requests never touch cookies and
|
|
40
|
+
* pages stay static. Outside a request (build, scripts) it returns `fallback`.
|
|
41
|
+
*/
|
|
42
|
+
async function inDraft<T>(read: () => Promise<T>, fallback: T): Promise<T> {
|
|
43
|
+
try {
|
|
44
|
+
return (await draftMode()).isEnabled ? await read() : fallback
|
|
45
|
+
} catch {
|
|
46
|
+
return fallback
|
|
47
|
+
}
|
|
48
|
+
}
|
|
24
49
|
|
|
25
50
|
const HASH_LENGTH = 32
|
|
26
51
|
const fingerprint = async (token: string) => (await sha256(token)).slice(0, HASH_LENGTH)
|
|
@@ -28,14 +53,14 @@ const fingerprint = async (token: string) => (await sha256(token)).slice(0, HASH
|
|
|
28
53
|
/** `edit` | `view`, or `<mode>:<authCookie>:<hash>` when bound to a session. */
|
|
29
54
|
async function modeValue(mode: OnPageMode, jar: Jar, bindTo?: string): Promise<string> {
|
|
30
55
|
const token = bindTo ? jar.get(bindTo)?.value : undefined
|
|
31
|
-
return token
|
|
56
|
+
return token ? `${mode}:${bindTo}:${await fingerprint(token)}` : mode
|
|
32
57
|
}
|
|
33
58
|
|
|
34
59
|
/** The mode a cookie value grants in this request, or null (unknown, or the bound session is gone). */
|
|
35
60
|
async function readMode(raw: string | undefined, jar: Jar): Promise<OnPageMode | null> {
|
|
36
61
|
if (!raw) return null
|
|
37
62
|
const [mode, bound, hash] = raw.split(':')
|
|
38
|
-
if (mode
|
|
63
|
+
if (!isOnPageMode(mode)) return null
|
|
39
64
|
if (bound) {
|
|
40
65
|
const token = jar.get(bound)?.value
|
|
41
66
|
if (!token || (await fingerprint(token)) !== hash) return null
|
|
@@ -49,13 +74,7 @@ export function nextSession(opts: NextSessionOptions = {}): SessionAdapter {
|
|
|
49
74
|
async enable(mode, { secure, bindTo }) {
|
|
50
75
|
;(await draftMode()).enable()
|
|
51
76
|
const jar = await cookies()
|
|
52
|
-
jar.set(COOKIE.mode, await modeValue(mode, jar, bindTo), {
|
|
53
|
-
httpOnly: true,
|
|
54
|
-
sameSite: 'lax',
|
|
55
|
-
secure,
|
|
56
|
-
path: '/',
|
|
57
|
-
maxAge,
|
|
58
|
-
})
|
|
77
|
+
jar.set(COOKIE.mode, await modeValue(mode, jar, bindTo), { ...cookieBase(secure), maxAge })
|
|
59
78
|
jar.delete(COOKIE.preview)
|
|
60
79
|
},
|
|
61
80
|
async enableLink({ secure, path: _path, expiresAt, seal }) {
|
|
@@ -66,13 +85,7 @@ export function nextSession(opts: NextSessionOptions = {}): SessionAdapter {
|
|
|
66
85
|
// Keep the draft secret server-side: the browser only gets the sealed grant.
|
|
67
86
|
jar.delete(COOKIE.nextDraft)
|
|
68
87
|
jar.delete(COOKIE.mode)
|
|
69
|
-
jar.set(COOKIE.preview, await seal(draft), {
|
|
70
|
-
httpOnly: true,
|
|
71
|
-
sameSite: 'lax',
|
|
72
|
-
secure,
|
|
73
|
-
path: '/',
|
|
74
|
-
expires: expiresAt,
|
|
75
|
-
})
|
|
88
|
+
jar.set(COOKIE.preview, await seal(draft), { ...cookieBase(secure), expires: expiresAt })
|
|
76
89
|
},
|
|
77
90
|
async disable() {
|
|
78
91
|
;(await draftMode()).disable()
|
|
@@ -85,41 +98,30 @@ export function nextSession(opts: NextSessionOptions = {}): SessionAdapter {
|
|
|
85
98
|
}
|
|
86
99
|
|
|
87
100
|
/** "edit", "view" or null for this request. Safe to call anywhere (returns null outside a request). */
|
|
88
|
-
export
|
|
89
|
-
|
|
90
|
-
if (!(await draftMode()).isEnabled) return null
|
|
101
|
+
export const getOnPageMode = (): Promise<OnPageMode | null> =>
|
|
102
|
+
inDraft(async () => {
|
|
91
103
|
const jar = await cookies()
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
return null
|
|
95
|
-
}
|
|
96
|
-
}
|
|
104
|
+
return readMode(jar.get(COOKIE.mode)?.value, jar)
|
|
105
|
+
}, null)
|
|
97
106
|
|
|
98
107
|
/**
|
|
99
108
|
* Should this request render drafts? `draftMode().isEnabled`, except that an
|
|
100
109
|
* editor whose session ended (signed out) no longer sees drafts. Draft mode
|
|
101
110
|
* turned on by something else (your own preview route) is respected.
|
|
102
111
|
*/
|
|
103
|
-
export
|
|
104
|
-
|
|
105
|
-
if (!(await draftMode()).isEnabled) return false
|
|
112
|
+
export const isDraftEnabled = (): Promise<boolean> =>
|
|
113
|
+
inDraft(async () => {
|
|
106
114
|
const jar = await cookies()
|
|
107
115
|
const raw = jar.get(COOKIE.mode)?.value
|
|
108
116
|
return raw === undefined || (await readMode(raw, jar)) !== null
|
|
109
|
-
}
|
|
110
|
-
return false
|
|
111
|
-
}
|
|
112
|
-
}
|
|
117
|
+
}, false)
|
|
113
118
|
|
|
114
119
|
/** Is this request a preview-link view (set by the proxy)? */
|
|
115
|
-
export
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return false
|
|
121
|
-
}
|
|
122
|
-
}
|
|
120
|
+
export const isSharedPreview = (): Promise<boolean> =>
|
|
121
|
+
inDraft(async () => (await headers()).get(HEADER.shared) === '1', false)
|
|
122
|
+
|
|
123
|
+
/** What `onpageContext()` adds to a Payload read in edit mode. */
|
|
124
|
+
export type OnPageReadContext = { context?: { [CONTEXT_KEY]: true } }
|
|
123
125
|
|
|
124
126
|
/**
|
|
125
127
|
* Spread into Payload reads that feed the page: in edit mode it turns on
|
|
@@ -127,6 +129,6 @@ export async function isSharedPreview(): Promise<boolean> {
|
|
|
127
129
|
*
|
|
128
130
|
* payload.find({ collection: 'pages', where, draft, ...(await onpageContext()) })
|
|
129
131
|
*/
|
|
130
|
-
export async function onpageContext(): Promise<
|
|
131
|
-
return (await getOnPageMode()) === 'edit' ? { context: {
|
|
132
|
+
export async function onpageContext(): Promise<OnPageReadContext> {
|
|
133
|
+
return (await getOnPageMode()) === 'edit' ? { context: { [CONTEXT_KEY]: true } } : {}
|
|
132
134
|
}
|