payload-live-preview 2.1.0 → 2.1.1
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 +15 -8
- 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/index.js +1 -1
- package/dist/adapters/nextjs/index.js.map +1 -1
- package/dist/adapters/nuxt/index.js +1 -1
- package/dist/adapters/nuxt/index.js.map +1 -1
- package/dist/adapters/sveltekit/index.js +1 -1
- package/dist/adapters/sveltekit/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 +5 -1
- package/dist/core.d.ts +5 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -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/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.d.cts +8 -3
- package/dist/server.d.ts +8 -3
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,12 +53,13 @@ Vite 5 through 8: that is what the supported framework majors install (Astro 7
|
|
|
53
53
|
|
|
54
54
|
The framework hooks and fragment renderers run twice on every push: at the floor of each peer range, as unit tests in jsdom that the `hook-matrix` job runs after installing the floors over the lockfile (React 18.0.0, Vue 3.3.0, Svelte 5.0.0), and at what the lockfile installs (React 19.2.8, Vue 3.5.42, Svelte 5.56.9), in the unit tests and the browser suites; the SvelteKit renderer is measured against a component compiled by `svelte/compiler` and rendered by the real `svelte/server`.
|
|
55
55
|
|
|
56
|
-
- Payload 2.32.3: wire corpus captured from a real admin and replayed in tests/integration/wire-corpus.test.ts, plus fieldSchemaJSON typing in tests/integration/schema-driven.test.ts; local
|
|
56
|
+
- Payload 2.32.3: wire corpus captured from a real admin and replayed in tests/integration/wire-corpus.test.ts, plus fieldSchemaJSON typing in tests/integration/schema-driven.test.ts; local release checks also exercise the real admin in DOM, React and Vue previews, each in frontend-only and serverURL modes (24 browser cases per variant), and run the React hook and the Vue composable through three lifecycle journeys (9 browser cases per variant) and seven guard journeys (21 browser cases per variant) in development builds; the legacy dependency graph retains upstream audit findings.
|
|
57
57
|
- Payload 3.85.0: wire corpus captured from a real admin, replayed in tests/integration/wire-corpus.test.ts.
|
|
58
58
|
- Payload 3.88.0: wire corpus captured from a real admin, replayed in tests/integration/wire-corpus.test.ts.
|
|
59
59
|
- Payload 3.89.0: wire corpus captured from a real admin, replayed in tests/integration/wire-corpus.test.ts.
|
|
60
60
|
- Payload 3.90.2: real admin E2E fixture (examples/payload-backend), run by CI on every push.
|
|
61
61
|
- Payload 4.0.0-canary.33: wire corpus captured from a real Payload 4 admin in a one-off upgrade round, replayed in tests/integration/wire-corpus.test.ts; the fixture itself stays on 3.x.
|
|
62
|
+
- Payload 4.0.0-beta.4 (pre-release): measured once on 2026-10-24, outside CI, in a consumer project that runs 2.1.0 from npm (Next 16, Node 24, Postgres 18, the real admin driven by headless Chromium). The config plugin type-checks against Payload's declarations and passes its config sanitizing. For six collection documents in two locales, the admin's preview frame loads the URL a hand-written callback produced, apart from the order of the query parameters, with the same breakpoint menu and no page errors, and the locale reaches the path resolvers. Against an admin with a `csrf` list, the `payload-session` strategy authorizes once its `origin` option is set. That run did not cover globals, the create screen, drafts and autosave, other plugins that write a Live Preview URL, or replacing existing breakpoints, and no CI job installs 4.0.0-beta.4.
|
|
62
63
|
- Payload latest: daily protocol watch executes @payloadcms/live-preview@latest against the corpus.
|
|
63
64
|
- Payload 4.0 pre-releases: daily protocol watch against @payloadcms/live-preview@canary, early warning only.
|
|
64
65
|
|
|
@@ -66,9 +67,11 @@ The framework hooks and fragment renderers run twice on every push: at the floor
|
|
|
66
67
|
|
|
67
68
|
Nuxt 4 has no row because no CI job installs it. A copy of the Nuxt example in Nuxt 4.5.2's `app/` layout passed the production suite against the mock admin on 2026-10-02 (54 of 54 cases), and a Nuxt 4.5.2 site installed from the 2.1.0 release candidate built and served its pages on 2026-10-18; neither was a real-admin run.
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
**A client-rendered app takes a hook.** `useLivePreviewDocument()` from `payload-live-preview/react` or `payload-live-preview/vue` returns the merged document and re-renders your tree. Payload 3 updates use the method-override merge request; Payload 2 scalar updates settle locally and raw relationships use schema-based collection GETs. A newer update aborts any request in flight; an HTTP error is refused instead of becoming the document; each hook has a session of its own, so two previews on one page keep their own documents ([docs/react.md](docs/react.md), [docs/vue.md](docs/vue.md)). The official [`@payloadcms/live-preview-react`](https://payloadcms.com/docs/live-preview/client) / `-vue` hooks ship in lockstep with Payload and remain the safe default if that matters more to you; running either alongside the DOM runtime on one page: [docs/interop.md](docs/interop.md).
|
|
71
|
+
|
|
72
|
+
### Local 2.1.0 release checks
|
|
70
73
|
|
|
71
|
-
The following checks passed on the 2.1.0
|
|
74
|
+
The following checks passed on 2026-10-23 on the package archive that was published as 2.1.0; the npm tarball differs from it only in the build timestamp. These are local results, separate from the CI jobs listed above. Browser counts include Chromium, Firefox and WebKit on Linux. `examples/payload-version-lanes` runs the Payload 2.32.3 rows, with `--probe=population`, `--probe=lifecycle` or `--probe=guards` for React and Vue.
|
|
72
75
|
|
|
73
76
|
| Version | Checked population | Passed cases |
|
|
74
77
|
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
|
|
@@ -76,7 +79,7 @@ The following checks passed on the 2.1.0 release candidate, the package archive
|
|
|
76
79
|
| Payload 2.32.3 | React and Vue hook lifecycle, frontend-only and serverURL | 9 per variant, four variants |
|
|
77
80
|
| Payload 2.32.3 | React and Vue hook guards, frontend-only and serverURL | 21 per variant, four variants |
|
|
78
81
|
| Node 20.19.0 | Peer-free consumer of the archive: every export without a peer under ESM and CommonJS, CLI help, inline script, signed tokens, `payload-session` and the fragment endpoint with its body, deadline and depth limits | 11 runtime cases |
|
|
79
|
-
| Next.js 16.
|
|
82
|
+
| Next.js 16.4.2 | Webpack production preview and fragment rendering | 54 browser cases |
|
|
80
83
|
| Astro 4.0.0 | Base adapter with loader and inline delivery, static output and scoped updates, two control builds | 36 browser cases |
|
|
81
84
|
| Astro 4.16.19 | Native React islands, CSP and navigation ownership | 12 browser cases |
|
|
82
85
|
|
|
@@ -84,8 +87,6 @@ The Node row runs the official 20.19.0 binary with no framework or optional peer
|
|
|
84
87
|
|
|
85
88
|
The two Astro rows can be repeated from the repository. The `astro-floor-base` and `astro-floor-islands` jobs run their specs on every push in Chromium ([Astro peer floors](docs/testing.md#astro-peer-floors)); the counts above are all three engines, with loader and inline delivery at five cases each and two control builds per engine for Astro 4.0.0.
|
|
86
89
|
|
|
87
|
-
**A client-rendered app takes a hook.** `useLivePreviewDocument()` from `payload-live-preview/react` or `payload-live-preview/vue` returns the merged document and re-renders your tree. Payload 3 updates use the method-override merge request; Payload 2 scalar updates settle locally and raw relationships use schema-based collection GETs. A newer update aborts any request in flight; an HTTP error is refused instead of becoming the document; each hook has a session of its own, so two previews on one page keep their own documents ([docs/react.md](docs/react.md), [docs/vue.md](docs/vue.md)). The official [`@payloadcms/live-preview-react`](https://payloadcms.com/docs/live-preview/client) / `-vue` hooks ship in lockstep with Payload and remain the safe default if that matters more to you; running either alongside the DOM runtime on one page: [docs/interop.md](docs/interop.md).
|
|
88
|
-
|
|
89
90
|
## Install
|
|
90
91
|
|
|
91
92
|
```bash
|
|
@@ -138,6 +139,10 @@ appears after the save loads the URL the plugin's function builds from the saved
|
|
|
138
139
|
document. `tests/real-payload/surface-create.spec.ts` checks this in Chromium,
|
|
139
140
|
Firefox and WebKit.
|
|
140
141
|
|
|
142
|
+
Payload 4 is still a pre-release. The plugin was measured once against
|
|
143
|
+
4.0.0-beta.4, outside CI; the [compatibility list](#compatibility) says what
|
|
144
|
+
that run covered and what it left out.
|
|
145
|
+
|
|
141
146
|
Use the lower-level callback when another plugin owns the surrounding config or
|
|
142
147
|
when a document may have no preview route:
|
|
143
148
|
|
|
@@ -231,6 +236,8 @@ That is it: the inline script detects the admin's iframe and starts patching. Ri
|
|
|
231
236
|
- **Nuxt** — a Nitro plugin plus a server handler: [docs/nuxt.md](docs/nuxt.md).
|
|
232
237
|
- **Plain HTML** — `generateInlineScript()` at build time: [docs/html.md](docs/html.md).
|
|
233
238
|
|
|
239
|
+
Whatever the framework, the admin also needs your frontend's URLs: the config plugin under [Configure Payload](#configure-payload) supplies them.
|
|
240
|
+
|
|
234
241
|
## Patch, fragment, route
|
|
235
242
|
|
|
236
243
|
A binding is patched in place by default. A `data-payload-fragment` boundary is rendered by your server from the unsaved form state instead — conditional sections, derived values, custom blocks, the component's own logic — and morphed into the page. Compatible retained nodes keep their focus and live properties; a node that changes kind, loses its pairing or crosses an ownership boundary is replaced and loses that state. The runtime posts the fields to the same-origin endpoint named in `fragments: { endpoint }`, built with `createFragmentEndpoint()`, which every adapter entry exports — Astro renders through its container API, Next.js through `react-dom/server`, SvelteKit through `svelte/server`, Nuxt through `vue/server-renderer` — and patches the boundary's own bindings when the server cannot render. With `routeStrategy: true` or `fragments` set, a binding in `<head>`, or one marked `data-payload-strategy="route"`, refreshes the whole route once per revision. The route morph follows the same retention rule and restores focus after a retained keyed move; it does not promise state for replaced nodes. Without either strategy the binding is patched like any other. The route is matched by top-level field name, so a dotted binding such as `meta.title` refreshes it only with `data-payload-depends="meta"`. Markup, endpoint, deployment requirements and the abuse model: [docs/hybrid.md](docs/hybrid.md).
|
|
@@ -284,7 +291,7 @@ Every event, transforms, custom field renderers, the built-in plugins and the pl
|
|
|
284
291
|
|
|
285
292
|
## Security model
|
|
286
293
|
|
|
287
|
-
- **Preview intent is not authorization.** The query parameter, the iframe destination and the referer are client-controlled; the adapters count the query alone by default. `allowedOrigins` governs browser `postMessage` senders and `shouldInject` filters routes; neither authenticates the request. `authorizePreviewRequest()` does, and where an adapter decides per request with `authorizePreview` — `createLivePreviewMiddleware()`, `livePreviewHandle()`, `livePreviewNitroPlugin()`, `defineLivePreviewServerHandler()` — that one result controls draft reads, `private, no-store` caching, CSP changes and runtime injection, and `await LivePreviewScript(…)` gates the Next.js script on the same verdict. Other setups decide less. Astro's build-time `inline` and `loader` modes and the synchronous `livePreviewScriptProps()` and `renderLivePreviewScript()` put the runtime or its bootstrap into every page that renders them, where it does not start outside a preview frame. Astro's `mode: 'middleware'` and the Nuxt module serialize their options, so they take the hook by module reference (`authorizePreviewModule`); with neither hook nor reference they refuse the strict default, and `
|
|
294
|
+
- **Preview intent is not authorization.** The query parameter, the iframe destination and the referer are client-controlled; the adapters count the query alone by default. `allowedOrigins` governs browser `postMessage` senders and `shouldInject` filters routes; neither authenticates the request. `authorizePreviewRequest()` does, and where an adapter decides per request with `authorizePreview` — `createLivePreviewMiddleware()`, `livePreviewHandle()`, `livePreviewNitroPlugin()`, `defineLivePreviewServerHandler()` — that one result controls draft reads, `private, no-store` caching, CSP changes and runtime injection, and `await LivePreviewScript(…)` gates the Next.js script on the same verdict. Other setups decide less. Astro's build-time `inline` and `loader` modes and the synchronous `livePreviewScriptProps()` and `renderLivePreviewScript()` put the runtime or its bootstrap into every page that renders them, where it does not start outside a preview frame. Astro's `mode: 'middleware'` and the Nuxt module serialize their options, so they take the hook by module reference (`authorizePreviewModule`); with neither hook nor reference they refuse the strict default, and `strict: false` (or the deprecated `defaults: 'v1'`) injects on intent alone.
|
|
288
295
|
- **Origin validation.** Every incoming message is checked against `allowedOrigins`; `document.referrer` is ignored by default, and after the first accepted update the inline runtime and `LivePreviewClient` lock to that origin (the React and Vue hooks do not: a message from a second listed origin is taken after the first, when the source policy lets its window speak). The adapters merge a `frame-ancestors` policy for the admin origins without clobbering the rest of your CSP.
|
|
289
296
|
- **Sanitization and URL validation.** Lexical and HTML writes in the browser pass a DOM sanitizer with a curated allow-list — `<script>`, `<form>`, `<iframe>`, `<svg>`, event handlers and `style` are rejected — and every `href`, `src`, `srcset` and `poster` must be `http(s)`, `mailto:`, `tel:` or relative; external links get `rel="noopener noreferrer"`. A server render sanitizes with the `document` it is given; without one, `lexicalToHtml()` returns custom renderers' markup as written and warns, and `requireDocument: true` makes it throw instead, the 3.0 default ([docs/security.md](docs/security.md#html-sanitization)).
|
|
290
297
|
- **Binding attributes are disclosure.** `data-payload-field` names a CMS field and `data-payload-owner` a document. `createPreviewBindings({ authorization })` suppresses them, companions included, on public responses. Never key CSS off `data-payload-*`.
|
|
@@ -297,7 +304,7 @@ Full details in [docs/security.md](docs/security.md). Report vulnerabilities per
|
|
|
297
304
|
- **Nothing updates.** Run `__livePreview.inspect()` in the preview iframe's console first — the handle exists once the injected runtime has started inside a preview frame; a page that starts `LivePreviewClient` itself calls `client.inspect()`. It names the cause in most cases. The common ones: the admin origin is missing from `allowedOrigins`, the page is not inside an iframe, the bound element does not exist (an empty field needs an anchor).
|
|
298
305
|
- **Relationship fields show IDs.** Set `serverURL` with `mergeDepth`; Payload 3.x posts unpopulated form values.
|
|
299
306
|
- **The preview iframe refuses to load.** The host sets `X-Frame-Options` or a restrictive `frame-ancestors`. The adapters merge `frame-ancestors` on authorized preview responses, and browsers frame by that directive alone; an `X-Frame-Options: DENY` from a proxy still blocks a preview response that has none, so it must go there.
|
|
300
|
-
- **The adapter refuses the configuration.** The strict default needs `authorizePreview`, a non-empty `allowedOrigins` (`https:` in production) and no referer trust; `defaults: 'v1'` stages a migration one row at a time.
|
|
307
|
+
- **The adapter refuses the configuration.** The strict default needs `authorizePreview`, a non-empty `allowedOrigins` (`https:` in production) and no referer trust; the deprecated `defaults: 'v1'` stages a migration one row at a time, and `pll migrate` writes out the rows it stands for.
|
|
301
308
|
|
|
302
309
|
`inspect()` readings, `pll doctor` and every diagnostic code: [docs/troubleshooting.md](docs/troubleshooting.md). `pll doctor` sends its preview request with `?preview=true`; a preview behind `authorizePreview` is audited with an editor's credential. A signed token goes in the URL as `?previewToken=…`, which the visitor request drops; `--header "x-preview-token: …"` works only where the token strategy sets `transport: { kind: 'header' }`; a Payload session is `--header "Cookie: payload-token=…"`.
|
|
303
310
|
|