payload-live-preview 2.3.1 → 2.4.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/README.md +13 -46
- package/dist/adapters/astro/index.js +1 -1
- package/dist/adapters/astro/index.js.map +1 -1
- package/dist/adapters/astro/middleware-entry.js +1 -1
- package/dist/adapters/astro/middleware-entry.js.map +1 -1
- package/dist/adapters/nextjs/client.d.ts +28 -0
- package/dist/adapters/nextjs/client.js +3 -0
- package/dist/adapters/nextjs/client.js.map +1 -0
- package/dist/adapters/nextjs/index.d.ts +34 -12
- package/dist/adapters/nextjs/index.js +1 -1
- package/dist/adapters/nextjs/index.js.map +1 -1
- package/dist/adapters/nuxt/client.d.ts +29 -0
- package/dist/adapters/nuxt/client.js +2 -0
- package/dist/adapters/nuxt/client.js.map +1 -0
- package/dist/adapters/nuxt/index.js +1 -1
- package/dist/adapters/nuxt/index.js.map +1 -1
- package/dist/adapters/nuxt/module.d.ts +23 -3
- package/dist/adapters/nuxt/module.js +1 -1
- package/dist/adapters/nuxt/module.js.map +1 -1
- package/dist/adapters/react/index.d.ts +2 -2
- package/dist/adapters/react/index.js +1 -1
- package/dist/adapters/react/index.js.map +1 -1
- package/dist/adapters/sveltekit/client.d.ts +37 -0
- package/dist/adapters/sveltekit/client.js +2 -0
- package/dist/adapters/sveltekit/client.js.map +1 -0
- package/dist/adapters/sveltekit/index.js +1 -1
- package/dist/adapters/sveltekit/index.js.map +1 -1
- package/dist/adapters/vue/index.d.ts +2 -2
- package/dist/adapters/vue/index.js +1 -1
- package/dist/adapters/vue/index.js.map +1 -1
- package/dist/client.cjs +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +22 -14
- package/dist/core.d.ts +22 -14
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/{document-session-BuYuO3ae.d.ts → document-session-oK92QjES.d.ts} +7 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lean.cjs +1 -1
- package/dist/lean.cjs.map +1 -1
- package/dist/lean.js +1 -1
- package/dist/lean.js.map +1 -1
- package/dist/lexical.cjs.map +1 -1
- package/dist/lexical.js.map +1 -1
- package/dist/plugins.cjs +1 -1
- package/dist/plugins.cjs.map +1 -1
- package/dist/plugins.js +1 -1
- package/dist/plugins.js.map +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.js.map +1 -1
- package/dist/structural.cjs.map +1 -1
- package/dist/structural.js.map +1 -1
- package/package.json +23 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `<LivePreviewNavigation />` — the App Router glue a Next preview needed in
|
|
5
|
+
* its own code before 2.4: it tells the runtime when a client navigation has
|
|
6
|
+
* committed and lends it `router.refresh` for route updates. Mount it once in
|
|
7
|
+
* a layout that stays mounted across the preview routes.
|
|
8
|
+
*
|
|
9
|
+
* This is the one entry that imports `next`: the hooks it needs exist only
|
|
10
|
+
* there, and passing them in as props would leave the glue where it was. `next`
|
|
11
|
+
* is an optional peer, and nothing else in the package imports it.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Renders nothing visible. It carries its own Suspense boundary because
|
|
16
|
+
* `useSearchParams()` suspends a prerendered route, and a boundary the caller
|
|
17
|
+
* forgets would turn that route dynamic or fail its build.
|
|
18
|
+
*
|
|
19
|
+
* ```tsx
|
|
20
|
+
* // app/layout.tsx
|
|
21
|
+
* import { LivePreviewNavigation } from 'payload-live-preview/nextjs/client';
|
|
22
|
+
* // inside <body>:
|
|
23
|
+
* <LivePreviewNavigation />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare function LivePreviewNavigation(): ReactNode;
|
|
27
|
+
|
|
28
|
+
export { LivePreviewNavigation };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import{createElement as n,Suspense as r,useRef as e,useEffect as t,useTransition as o}from"react";import{useRouter as u,usePathname as i,useSearchParams as c}from"next/navigation";var l="__livePreviewRouteRefresh";function f(n){const r=n.current;n.current=[];for(const n of r)n()}function s({refresh:n}){const[r,u]=o(),i=e([]);return t(()=>{r||f(i)}),t(()=>{const r=function registerRouteRefresh(n){const r="undefined"==typeof window?void 0:window;return void 0===r?()=>{}:(r[l]=n,()=>{r[l]===n&&(r[l]=void 0)})}(()=>new Promise(r=>{i.current.push(r),u(()=>{n()})}));return()=>{r(),f(i)}},[n,u]),null}function v(){const r=u(),o=function(){const n=i(),r=c();return null===n||null===r?null:`${n}?${r.toString()}`}(),l=e(null);return t(()=>{if(null===o)return;const n=l.current;l.current=o,null!==n&&n!==o&&document.dispatchEvent(new Event("payload-live-preview:navigation"))},[o]),n(s,{refresh:r.refresh})}function a(){return n(r,{fallback:null},n(v))}export{a as LivePreviewNavigation};
|
|
3
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":["SLOT","settle","waiting","settled","current","resolve","LivePreviewRouteRefresh","refresh","pending","startTransition","useTransition","useRef","useEffect","undo","registerRouteRefresh","target","window","Promise","push","NavigationBridge","router","useRouter","key","pathname","usePathname","search","useSearchParams","toString","useCommittedKey","committed","previous","document","dispatchEvent","Event","createElement","LivePreviewNavigation","Suspense","fallback"],"sources":["../../../src/core/navigation-lifecycle.ts","../../../src/core/route-refresh.ts","../../../src/adapters/react/route-refresh.ts","../../../src/adapters/nextjs/client.ts"],"sourcesContent":["/**\n * Binds the document lifecycle the runtime cannot see: a back/forward-cache\n * restore re-runs no scripts, so a client that stayed attached comes back\n * bound to a frozen document and quietly stops updating; a soft navigation\n * replaces the content the binding cache describes. Both are silent failures.\n */\n\n/** The part of the client this owner drives. */\nexport interface NavigationLifecycleTarget {\n suspend(): boolean;\n resume(): boolean;\n /** Re-scan bindings after replacing markup without crossing a navigation boundary. */\n refreshCache(): void;\n /** Re-scan and replay the last accepted document after a committed router navigation. */\n refreshAfterNavigation?(): void;\n}\n\n/** Package-owned event dispatched after a framework router commit. @internal */\nexport const NAVIGATION_COMMIT_EVENT = 'payload-live-preview:navigation';\nconst ASTRO_INITIAL_PAGE_LOAD_EVENT = 'astro:page-load';\nconst ASTRO_AFTER_SWAP_EVENT = 'astro:after-swap';\n\nexport interface NavigationLifecycleOptions {\n /** Where `pagehide`/`pageshow` fire; they exist only on the window. Defaults to `window`. */\n readonly windowTarget?: EventTarget;\n /** Event target for soft-navigation events. Defaults to `document`. */\n readonly documentTarget?: EventTarget;\n /**\n * Events after which the DOM was replaced and the current editor state must\n * be replayed, e.g. `astro:page-load`. Empty by default: only the host knows\n * which its router fires.\n */\n readonly softNavigationEvents?: readonly string[];\n}\n\n/** Returns an unbind function; calling it twice is harmless. */\nexport function bindNavigationLifecycle(\n target: NavigationLifecycleTarget,\n options: NavigationLifecycleOptions = {},\n): () => void {\n const windowTarget = options.windowTarget ?? (globalThis as { window?: EventTarget }).window;\n const documentTarget =\n options.documentTarget ?? (globalThis as { document?: EventTarget }).document;\n const softEvents = [...new Set(options.softNavigationEvents ?? [])];\n\n const onPageHide = (): void => {\n target.suspend();\n };\n const onPageShow = (event: Event): void => {\n // Only a persisted restore needs reacquiring; an ordinary load already started.\n if ((event as Event & { readonly persisted?: boolean }).persisted === true) {\n target.resume();\n }\n };\n let astroNavigationArmed = false;\n const onAstroAfterSwap = (): void => {\n astroNavigationArmed = true;\n };\n const onSoftNavigation = (event: Event): void => {\n // Astro emits page-load for the initial document too. A client-router\n // commit is the page-load preceded by after-swap, including before the\n // browser's initial `load` when a subresource is slow.\n if (event.type === ASTRO_INITIAL_PAGE_LOAD_EVENT) {\n if (!astroNavigationArmed) return;\n astroNavigationArmed = false;\n }\n if (target.refreshAfterNavigation === undefined) target.refreshCache();\n else target.refreshAfterNavigation();\n };\n\n let unbound = false;\n\n windowTarget?.addEventListener('pagehide', onPageHide);\n windowTarget?.addEventListener('pageshow', onPageShow);\n for (const name of softEvents) documentTarget?.addEventListener(name, onSoftNavigation);\n const observesAstro = softEvents.includes(ASTRO_INITIAL_PAGE_LOAD_EVENT);\n if (observesAstro) documentTarget?.addEventListener(ASTRO_AFTER_SWAP_EVENT, onAstroAfterSwap);\n\n return (): void => {\n if (unbound) return;\n unbound = true;\n windowTarget?.removeEventListener('pagehide', onPageHide);\n windowTarget?.removeEventListener('pageshow', onPageShow);\n for (const name of softEvents) documentTarget?.removeEventListener(name, onSoftNavigation);\n if (observesAstro) {\n documentTarget?.removeEventListener(ASTRO_AFTER_SWAP_EVENT, onAstroAfterSwap);\n }\n };\n}\n","/**\n * The seam between a host framework's own router and the route strategy.\n *\n * The route strategy answers a revision by fetching the route and morphing it\n * into the living page. On a page whose DOM belongs to a reconciler that is\n * working against the framework, and it was seen to break once: `removeChild`\n * on `null` inside React's commit phase, over markup this package had patched.\n * A host that re-renders the route itself does the same job without the morph\n * and without the second HTML request, so it is given the chance to.\n *\n * Why a named slot on `window` and not a reference: the runtime is an inline\n * script that builds its strategy before any application code has hydrated, so\n * there is nothing to hand a component; `window.__livePreview`, the only other\n * public surface, is created later and not at all outside a preview; and the\n * two richer extension points — plugins and events — are reachable only through\n * the programmatic client, which a page using an adapter does not have. One\n * slot, written by whoever mounts and read at the moment of a refresh, is the\n * smallest thing that survives both orders of arrival.\n */\n\n/**\n * A host's own route refresh. It must settle once the fresh markup is\n * committed: the runtime re-applies the revision on top of it, and doing that\n * against the markup being replaced would write into nodes about to be dropped.\n * A function that returns nothing is taken as already committed; the route\n * strategy says once that it could not wait (LP0810), and 3.0 requires the\n * promise (ADR 0028).\n */\nexport type RouteRefresh = () => void | Promise<void>;\n\n/** Read late, so it does not matter whether the runtime or the component came first. */\nconst SLOT = '__livePreviewRouteRefresh';\n\ntype WindowSlot = Record<string, RouteRefresh | undefined>;\n\nfunction slot(): WindowSlot | undefined {\n return typeof window === 'undefined' ? undefined : (window as unknown as WindowSlot);\n}\n\n/**\n * Lend the runtime the host's route refresh; the return value undoes it, for a\n * component's cleanup. A later registration wins, and undoing one that has\n * already been replaced does nothing — two components on one page would\n * otherwise take the slot from each other on unmount.\n */\nexport function registerRouteRefresh(refresh: RouteRefresh): () => void {\n const target = slot();\n if (target === undefined) return () => undefined;\n target[SLOT] = refresh;\n return () => {\n // Cleared rather than deleted: the slot is read through `?.` anyway, and\n // removing the key buys nothing a lint rule would not have to be argued out of.\n if (target[SLOT] === refresh) target[SLOT] = undefined;\n };\n}\n\n/** The registered refresh, or `undefined` on a page that registered none. */\nexport function readRouteRefresh(): RouteRefresh | undefined {\n return slot()?.[SLOT];\n}\n","/**\n * `<LivePreviewRouteRefresh />` — lends the runtime the host router's own\n * refresh, so a route update is a re-render the framework performs rather than\n * fresh HTML this package morphs over the reconciler's nodes (see\n * `src/core/route-refresh.ts` for why that is worth avoiding).\n *\n * It takes the refresh as a prop instead of reaching for `next/navigation`,\n * because `next` is not a dependency of this package and must not become one\n * for a component this small. In Next's App Router that is one client\n * component:\n *\n * ```tsx\n * 'use client';\n * import { useRouter } from 'next/navigation';\n * import { LivePreviewRouteRefresh } from 'payload-live-preview/react';\n *\n * export function LivePreviewRefresh() {\n * return <LivePreviewRouteRefresh refresh={useRouter().refresh} />;\n * }\n * ```\n *\n * The same component serves any router with a refresh of that shape.\n */\n\nimport { useEffect, useRef, useTransition } from 'react';\nimport { registerRouteRefresh } from '@core/route-refresh';\n\nexport interface LivePreviewRouteRefreshProps {\n /** The host router's refresh — `useRouter().refresh` in Next's App Router. */\n readonly refresh: () => void;\n}\n\n/** Resolve everyone waiting on the transition that has just finished. */\nfunction settle(waiting: { current: (() => void)[] }): void {\n const settled = waiting.current;\n waiting.current = [];\n for (const resolve of settled) resolve();\n}\n\n/**\n * Renders nothing. Mount it once inside the preview tree; a second one takes\n * the registration from the first, and unmounting gives it back.\n */\nexport function LivePreviewRouteRefresh({ refresh }: LivePreviewRouteRefreshProps): null {\n const [pending, startTransition] = useTransition();\n const waiting = useRef<(() => void)[]>([]);\n\n // The runtime re-applies the revision on top of the fresh markup, so the\n // refresh it is given may not resolve before that markup is committed. The\n // transition is what makes that observable: it is pending until React has\n // rendered the new server output, and this effect runs on the commit after.\n useEffect(() => {\n if (!pending) settle(waiting);\n });\n\n useEffect(() => {\n const undo = registerRouteRefresh(\n () =>\n new Promise<void>((resolve) => {\n waiting.current.push(resolve);\n startTransition(() => {\n refresh();\n });\n }),\n );\n // A tree that unmounts mid-refresh will never commit one, so the waiting\n // runtime is released here rather than left holding a promise for good.\n return () => {\n undo();\n settle(waiting);\n };\n }, [refresh, startTransition]);\n\n return null;\n}\n","/**\n * `<LivePreviewNavigation />` — the App Router glue a Next preview needed in\n * its own code before 2.4: it tells the runtime when a client navigation has\n * committed and lends it `router.refresh` for route updates. Mount it once in\n * a layout that stays mounted across the preview routes.\n *\n * This is the one entry that imports `next`: the hooks it needs exist only\n * there, and passing them in as props would leave the glue where it was. `next`\n * is an optional peer, and nothing else in the package imports it.\n */\n\nimport { Suspense, createElement, useEffect, useRef, type ReactNode } from 'react';\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation';\nimport { NAVIGATION_COMMIT_EVENT } from '@core/navigation-lifecycle';\nimport { LivePreviewRouteRefresh } from '@adapters/react/route-refresh';\n\n/** The committed URL, or `null` while the router cannot name it yet. */\nfunction useCommittedKey(): string | null {\n const pathname = usePathname();\n const search = useSearchParams();\n return pathname === null || search === null ? null : `${pathname}?${search.toString()}`;\n}\n\nfunction NavigationBridge(): ReactNode {\n const router = useRouter();\n const key = useCommittedKey();\n const committed = useRef<string | null>(null);\n\n // The first key is the page the runtime started on, and StrictMode runs this\n // effect twice for it: only a changed key is a navigation. A cancelled\n // transition never changes the key, so it dispatches nothing.\n useEffect(() => {\n if (key === null) return;\n const previous = committed.current;\n committed.current = key;\n if (previous === null || previous === key) return;\n document.dispatchEvent(new Event(NAVIGATION_COMMIT_EVENT));\n }, [key]);\n\n return createElement(LivePreviewRouteRefresh, { refresh: router.refresh });\n}\n\n/**\n * Renders nothing visible. It carries its own Suspense boundary because\n * `useSearchParams()` suspends a prerendered route, and a boundary the caller\n * forgets would turn that route dynamic or fail its build.\n *\n * ```tsx\n * // app/layout.tsx\n * import { LivePreviewNavigation } from 'payload-live-preview/nextjs/client';\n * // inside <body>:\n * <LivePreviewNavigation />\n * ```\n */\nexport function LivePreviewNavigation(): ReactNode {\n return createElement(Suspense, { fallback: null }, createElement(NavigationBridge));\n}\n"],"mappings":";oLAkBO,ICaDA,EAAO,4BCEb,SAASC,EAAOC,GACd,MAAMC,EAAUD,EAAQE,QACxBF,EAAQE,QAAU,GAClB,UAAWC,KAAWF,EAASE,GACjC,CAMO,SAASC,GAAwBC,QAAEA,IACxC,MAAOC,EAASC,GAAmBC,IAC7BR,EAAUS,EAAuB,IA4BvC,OAtBAC,EAAU,KACHJ,GAASP,EAAOC,KAGvBU,EAAU,KACR,MAAMC,EDXH,SAASC,qBAAqBP,GACnC,MAAMQ,EAVmB,oBAAXC,YAAyB,EAAaA,OAWpD,YAAe,IAAXD,EAA6B,QACjCA,EAAOf,GAAQO,EACR,KAGDQ,EAAOf,KAAUO,IAASQ,EAAOf,QAAQ,IAEjD,CCEiBc,CACX,IACE,IAAIG,QAAeZ,IACjBH,EAAQE,QAAQc,KAAKb,GACrBI,EAAgB,KACdF,SAMR,MAAO,KACLM,IACAZ,EAAOC,KAER,CAACK,EAASE,IAEN,IACT,CCnDA,SAASU,IACP,MAAMC,EAASC,IACTC,EARR,WACE,MAAMC,EAAWC,IACXC,EAASC,IACf,OAAoB,OAAbH,GAAgC,OAAXE,EAAkB,KAAO,GAAGF,KAAYE,EAAOE,YAC7E,CAIcC,GACNC,EAAYlB,EAAsB,MAaxC,OARAC,EAAU,KACR,GAAY,OAARU,EAAc,OAClB,MAAMQ,EAAWD,EAAUzB,QAC3ByB,EAAUzB,QAAUkB,EACH,OAAbQ,GAAqBA,IAAaR,GACtCS,SAASC,cAAc,IAAIC,MHlBQ,qCGmBlC,CAACX,IAEGY,EAAc5B,EAAyB,CAAEC,QAASa,EAAOb,SAClE,CAcO,SAAS4B,IACd,OAAOD,EAAcE,EAAU,CAAEC,SAAU,MAAQH,EAAcf,GACnE,Q","ignoreList":[]}
|
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import { P as PreviewAdapterOptions } from '../../options-DTzhFVJ4.js';
|
|
2
2
|
import { F as FragmentEndpointOptions$1, a as FragmentRegistry$1, b as FragmentRegistryEntry$1, c as FragmentRenderer$1, d as FragmentRenderInput } from '../../fragment-endpoint-CQE_7MKi.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* What a Next `<script>` element carries for the runtime, shared by the
|
|
6
|
+
* synchronous `livePreviewScriptProps()` and the `LivePreviewScript` component.
|
|
7
|
+
* It lives apart so the component builds the same props without the helper's
|
|
8
|
+
* deprecation notice: the component renders them only for an authorized
|
|
9
|
+
* request, the helper for everyone who renders the layout.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** What a `<script>` element needs to carry the runtime; see `livePreviewScriptProps`. */
|
|
13
|
+
interface LivePreviewScriptProps {
|
|
14
|
+
readonly dangerouslySetInnerHTML: {
|
|
15
|
+
readonly __html: string;
|
|
16
|
+
};
|
|
17
|
+
/** Present only when a nonce was passed, so React omits the attribute otherwise. */
|
|
18
|
+
readonly nonce?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
4
21
|
/**
|
|
5
22
|
* Next.js adapter. The script goes into the root layout, because
|
|
6
23
|
* `NextResponse.next()` carries no body for the middleware to inject into:
|
|
@@ -16,23 +33,28 @@ type LivePreviewNextOptions = PreviewAdapterOptions;
|
|
|
16
33
|
* `authorizePreviewRequest()` in the route when a page needs it.
|
|
17
34
|
*/
|
|
18
35
|
declare function createLivePreviewMiddleware(options?: LivePreviewNextOptions): (request: Request, response: Response) => Promise<Response>;
|
|
19
|
-
|
|
20
|
-
interface LivePreviewScriptProps {
|
|
21
|
-
readonly dangerouslySetInnerHTML: {
|
|
22
|
-
readonly __html: string;
|
|
23
|
-
};
|
|
24
|
-
/** Present only when a nonce was passed, so React omits the attribute otherwise. */
|
|
25
|
-
readonly nonce?: string;
|
|
26
|
-
}
|
|
36
|
+
|
|
27
37
|
/**
|
|
28
|
-
* Props for the `<script>` in `app/layout.tsx`:
|
|
38
|
+
* Props for the `<script>` in `app/layout.tsx`, with `delivery: 'asset'`: the
|
|
39
|
+
* page carries a bootstrap that fetches the runtime only in a preview context.
|
|
29
40
|
*
|
|
30
41
|
* ```tsx
|
|
31
|
-
* <script {...livePreviewScriptProps({
|
|
42
|
+
* <script {...livePreviewScriptProps({ ...livePreviewOptions, delivery: 'asset' })} />
|
|
32
43
|
* ```
|
|
44
|
+
*/
|
|
45
|
+
declare function livePreviewScriptProps(options: LivePreviewNextOptions & {
|
|
46
|
+
readonly delivery: 'asset';
|
|
47
|
+
readonly nonce?: string;
|
|
48
|
+
}): LivePreviewScriptProps;
|
|
49
|
+
/**
|
|
50
|
+
* Props for a `<script>` that carries the whole runtime. A synchronous helper
|
|
51
|
+
* cannot await an authorization verdict, so a root layout that renders it
|
|
52
|
+
* hands the runtime to every visitor, twice with React's flight payload.
|
|
33
53
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
54
|
+
* @deprecated 3.0 removes inline delivery from this helper (ADR 0007, row 32). Render
|
|
55
|
+
* `await LivePreviewScript({ request, ...options })` from this entry instead, which renders
|
|
56
|
+
* nothing for a request the policy does not authorize, or pass `delivery: 'asset'`;
|
|
57
|
+
* `renderLivePreviewScript()` stays for HTML a server builds as a string.
|
|
36
58
|
*/
|
|
37
59
|
declare function livePreviewScriptProps(options?: LivePreviewNextOptions & {
|
|
38
60
|
readonly nonce?: string;
|