payload-live-preview 2.0.6 → 2.1.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 +141 -38
- package/dist/adapters/astro/FragmentBridge.astro +25 -0
- package/dist/adapters/astro/PreviewBoundary.astro +2 -4
- package/dist/adapters/astro/RichText.astro +26 -5
- package/dist/adapters/astro/index.d.ts +41 -9
- package/dist/adapters/astro/index.js +1 -1
- package/dist/adapters/astro/index.js.map +1 -1
- package/dist/adapters/astro/middleware-entry.d.ts +3 -3
- package/dist/adapters/astro/middleware-entry.js +1 -1
- package/dist/adapters/astro/middleware-entry.js.map +1 -1
- package/dist/adapters/nextjs/index.d.ts +17 -12
- package/dist/adapters/nextjs/index.js +1 -1
- package/dist/adapters/nextjs/index.js.map +1 -1
- package/dist/adapters/nuxt/index.d.ts +11 -6
- package/dist/adapters/nuxt/index.js +1 -1
- package/dist/adapters/nuxt/index.js.map +1 -1
- package/dist/adapters/nuxt/module.d.ts +64 -13
- package/dist/adapters/nuxt/module.js +1 -1
- package/dist/adapters/nuxt/module.js.map +1 -1
- package/dist/adapters/react/index.d.ts +11 -7
- package/dist/adapters/react/index.js +1 -1
- package/dist/adapters/react/index.js.map +1 -1
- package/dist/adapters/sveltekit/index.d.ts +10 -5
- package/dist/adapters/sveltekit/index.js +1 -1
- package/dist/adapters/sveltekit/index.js.map +1 -1
- package/dist/adapters/vue/index.d.ts +10 -7
- package/dist/adapters/vue/index.js +1 -1
- package/dist/adapters/vue/index.js.map +1 -1
- package/dist/annotate.d.ts +15 -2
- package/dist/annotate.js.map +1 -1
- package/dist/client.cjs +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +367 -266
- package/dist/client.d.ts +367 -266
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/codegen-cli.js.map +1 -1
- package/dist/codegen.cjs.map +1 -1
- package/dist/codegen.d.cts +7 -1
- package/dist/codegen.d.ts +7 -1
- package/dist/codegen.js.map +1 -1
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +544 -392
- package/dist/core.d.ts +544 -392
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/doctor-cli.d.ts +1 -1
- package/dist/doctor-cli.js +1 -1
- package/dist/doctor-cli.js.map +1 -1
- package/dist/doctor.d.ts +2 -2
- package/dist/doctor.js +1 -1
- package/dist/doctor.js.map +1 -1
- package/dist/{document-session-BfBfLVpg.d.ts → document-session-DWi9H3Qk.d.ts} +7 -0
- package/dist/{fragment-endpoint-DATKU183.d.ts → fragment-endpoint-B2qcRw5T.d.ts} +22 -7
- package/dist/fragment.cjs +1 -1
- package/dist/fragment.cjs.map +1 -1
- package/dist/fragment.d.cts +37 -16
- package/dist/fragment.d.ts +37 -16
- package/dist/fragment.js +1 -1
- package/dist/fragment.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +598 -400
- package/dist/index.d.ts +598 -400
- 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.d.cts +5 -2
- package/dist/lean.d.ts +5 -2
- package/dist/lean.js +1 -1
- package/dist/lean.js.map +1 -1
- package/dist/lexical.cjs +1 -1
- package/dist/lexical.cjs.map +1 -1
- package/dist/lexical.d.cts +32 -11
- package/dist/lexical.d.ts +32 -11
- package/dist/lexical.js +1 -1
- package/dist/lexical.js.map +1 -1
- package/dist/{locals-CNUGYbA_.d.ts → locals-B1mF3aTb.d.ts} +1 -1
- package/dist/{middleware-CWObDO37.d.ts → middleware-BGobLN9P.d.ts} +11 -5
- package/dist/migrate.js +1 -1
- package/dist/migrate.js.map +1 -1
- package/dist/{options-uHKrDM2U.d.ts → options-Cm8fC7dj.d.ts} +31 -1
- package/dist/payload.cjs +1 -1
- package/dist/payload.cjs.map +1 -1
- package/dist/payload.d.cts +18 -2
- package/dist/payload.d.ts +18 -2
- package/dist/payload.js +1 -1
- package/dist/payload.js.map +1 -1
- package/dist/plugin.cjs +2 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.d.cts +30 -0
- package/dist/plugin.d.ts +30 -0
- package/dist/plugin.js +2 -0
- package/dist/plugin.js.map +1 -0
- package/dist/plugins.cjs +1 -1
- package/dist/plugins.cjs.map +1 -1
- package/dist/plugins.d.cts +102 -29
- package/dist/plugins.d.ts +102 -29
- package/dist/plugins.js +1 -1
- package/dist/plugins.js.map +1 -1
- package/dist/{probe-Bzm4fYFL.d.ts → probe-DGPDsnbj.d.ts} +6 -2
- package/dist/server.cjs +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +43 -4
- package/dist/server.d.ts +43 -4
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/structural.cjs +2 -2
- package/dist/structural.cjs.map +1 -1
- package/dist/structural.d.cts +61 -13
- package/dist/structural.d.ts +61 -13
- package/dist/structural.js +2 -2
- package/dist/structural.js.map +1 -1
- package/package.json +33 -4
package/README.md
CHANGED
|
@@ -10,50 +10,50 @@
|
|
|
10
10
|
|
|
11
11
|
> **Live preview for Payload CMS on server-rendered and static sites** — Astro, Next.js, SvelteKit, Nuxt and plain HTML.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Payload publishes the framework-independent `@payloadcms/live-preview` client and the official `@payloadcms/live-preview-react` and `@payloadcms/live-preview-vue` wrappers. Those hooks feed a hydrated component tree; they do not own server markup outside that tree. This package covers that markup. Mark one boundary per server-rendered component, or individual fields on a static page, and edits reach the iframe as the editor types. Scalar patches and successful fragment renders need neither a rebuild nor a full-page reload; a route-level change uses the configured route refresh. Client-rendered apps can instead use this package's React or Vue entry. Its v2 sanitizer and request-time policy defaults are strict; static delivery has no request to authorize. The package has no direct dependencies; framework entries use optional peers.
|
|
14
14
|
|
|
15
|
-
One runtime serves every framework, and every framework adapter's example app runs end to end in Chromium, Firefox and WebKit on every push to `main` and on pull requests. Astro has the most coverage: the widest version matrix (4.x to 7.x), the
|
|
15
|
+
One runtime serves every framework, and every framework adapter's example app runs end to end in Chromium, Firefox and WebKit on every push to `main` and on pull requests. Astro has the most coverage: the widest version matrix (4.x to 7.x), the `RichText` and `PreviewBoundary` components, and the annotator as a Vite plugin (`pll-codegen annotate` itself also reads `.jsx`, `.tsx` and `.svelte`).
|
|
16
16
|
|
|
17
17
|
**New here? Start with the [Astro guide](docs/astro.md), or pick your framework in the [documentation index](docs/README.md).**
|
|
18
18
|
|
|
19
19
|
## Highlights
|
|
20
20
|
|
|
21
|
-
- **One runtime, every frontend.** One TypeScript runtime compiled to a self-contained inline script of about
|
|
21
|
+
- **One runtime, every frontend.** One TypeScript runtime compiled to a self-contained inline script of about 44 KB gzip — 36 KB with the lean artifact, 47 to 49 KB once the route or fragment prelude is appended ([docs/options.md](docs/options.md)). The adapters for Astro, Next.js, SvelteKit and Nuxt deliver it; where they decide per request they also authorize the request, merge `frame-ancestors` into your CSP and mark the changed response `private, no-store`. They provide the fragment endpoint and, outside Astro, the route that serves the runtime asset.
|
|
22
22
|
- **Patch, fragment, route.** A binding is patched in place; a `data-payload-fragment` boundary is rendered again by your server from the unsaved form state; a patch that fell short, or a change nothing binds, goes to the fragment render or a route refresh when the page has one ([below](#patch-fragment-route)).
|
|
23
|
-
- **Delivery.** Inline, or a bootstrap that fetches the content-hashed, SRI-verified runtime only inside a preview (`delivery: 'asset'`; Astro's static equivalent is `mode: 'loader'`). Where something decides per request — Astro's middleware,
|
|
23
|
+
- **Delivery.** Inline, or a bootstrap that fetches the content-hashed, SRI-verified runtime only inside a preview (`delivery: 'asset'`; Astro's static equivalent is `mode: 'loader'`). Where something decides per request — Astro's middleware, `await LivePreviewScript(…)` in Next.js, the SvelteKit handle, the Nuxt Nitro plugin — a public visitor receives no script at all ([docs/deployment.md](docs/deployment.md#what-a-public-visitor-pays)).
|
|
24
24
|
- **Payload 3.x native.** `serverURL` re-fetches the populated document, like the official client, so relationship and upload fields render as content rather than as IDs — but only when an edit needs one: in the DOM runtime, typing into a text field costs no request, and a burst on a relationship costs two requests, at its start and its end, rather than one per keystroke.
|
|
25
|
-
- **Lexical renderer.** 20 node types including `upload`, `relationship`, `block`, `autolink`, tabs, indent and RTL, with
|
|
25
|
+
- **Lexical renderer.** 20 node types including `upload`, `relationship`, `block`, `autolink`, tabs, indent and RTL, with rich-text detection from the value shape (an element that can hold uploads or relationships needs `data-payload-richtext`). An unknown node renders only its children; a block with no registered renderer becomes an empty placeholder, and the live write keeps the markup your server rendered in its place when the two trees line up. The prebuilt inline runtime cannot register block renderers.
|
|
26
26
|
- **Hooks for client-rendered apps.** `useLivePreviewDocument()` from `payload-live-preview/react` or `/vue` hands the merged document to your component tree ([docs/react.md](docs/react.md), [docs/vue.md](docs/vue.md)).
|
|
27
27
|
- **Tooling.** `pll doctor <url>` requests a page as a visitor and as the admin's iframe and reports the difference; `pll migrate` rewrites 1.x APIs to their 2.0 names; `pll-codegen` generates types from the Payload config, and `pll-codegen annotate` finds the bindings a template can take unambiguously.
|
|
28
28
|
- **Authorization before anything privileged.** Where an adapter decides per request with `authorizePreview`, draft reads, runtime injection, CSP changes and binding attributes follow one verified decision, and the client-controlled intent signals unlock nothing. The setups that decide less are named under [Security model](#security-model).
|
|
29
29
|
- **Strict by default.** Escape-by-default sanitizer, URL and `srcset` validation, policed attribute writes, prototype-pollution guards, per-instance clients, typed bindings with `pll-codegen`.
|
|
30
|
-
- **Also in the box.** `revealEditedField` scrolls the field being typed in into view ([docs/reveal.md](docs/reveal.md)); a hydrated island keeps its subtree and re-renders itself from a `payload-live-preview:update` event ([docs/hybrid.md](docs/hybrid.md#islands-on-the-same-page)); `data-payload-owner` scopes bindings on a page that previews several documents, `PreviewBoundary` keeps an anchor for a field that is still empty, and `autoBind: 'unique'` finds bindings by value ([docs/bindings.md](docs/bindings.md)); array items render from a template through a keyed morph (`payload-live-preview/structural`); an `aria-live` region announces connect, update and disconnect; the runtime binds again after a back/forward-cache restore or a soft navigation, and the Next.js and Nuxt adapters wait for hydration before the first write.
|
|
30
|
+
- **Also in the box.** `revealEditedField` scrolls the field being typed in into view ([docs/reveal.md](docs/reveal.md)); a hydrated island keeps its subtree and re-renders itself from a `payload-live-preview:update` event ([docs/hybrid.md](docs/hybrid.md#islands-on-the-same-page)); `data-payload-owner` scopes bindings on a page that previews several documents, `PreviewBoundary` keeps an anchor for a field that is still empty, and `autoBind: 'unique'` finds bindings by value ([docs/bindings.md](docs/bindings.md)); array items render from a template through a keyed morph (`payload-live-preview/structural`); an `aria-live` region announces connect, update and disconnect; the runtime binds again after a back/forward-cache restore or a soft navigation, and the Next.js, SvelteKit and Nuxt adapters wait for hydration before the first write.
|
|
31
31
|
|
|
32
32
|
## Compatibility
|
|
33
33
|
|
|
34
|
-
| | Payload 2.x
|
|
35
|
-
| ---------------------------------------------- |
|
|
36
|
-
| Scalar field updates | ✅
|
|
37
|
-
| Rich text (Lexical) | ✅
|
|
38
|
-
| Relationship / upload population |
|
|
39
|
-
| Schema-driven field typing (`fieldSchemaJSON`) | ✅
|
|
34
|
+
| | Payload 2.x | Payload 3.x |
|
|
35
|
+
| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
|
|
36
|
+
| Scalar field updates | ✅ | ✅ |
|
|
37
|
+
| Rich text (Lexical) | ✅ | ✅ |
|
|
38
|
+
| Relationship / upload population | Collection GETs from the accepted schema with `serverURL` and matching depth (relationships measured against a real 2.32.3 admin, uploads by unit tests) | ✅ with `serverURL` (REST merge) |
|
|
39
|
+
| Schema-driven field typing (`fieldSchemaJSON`) | ✅ | — (3.x removed it; DOM heuristics + Lexical auto-detection take over) |
|
|
40
40
|
|
|
41
41
|
<!-- compat-matrix:start -->
|
|
42
42
|
|
|
43
|
-
| Framework | Supported
|
|
44
|
-
| --------- |
|
|
45
|
-
| Astro | >=4.0.0 <8.0.0
|
|
46
|
-
| Next.js | App Router, 16
|
|
47
|
-
| SvelteKit | 2.x
|
|
48
|
-
| Nuxt | 3.x
|
|
43
|
+
| Framework | Supported | Tested in CI on every push (version, browsers) |
|
|
44
|
+
| --------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
45
|
+
| Astro | >=4.0.0 <8.0.0 | 7.3.2 (chromium, firefox, webkit); 6.x (chromium); 5.x (chromium); 4.x (chromium) |
|
|
46
|
+
| Next.js | App Router, 15.5 and 16 | 16.4.2 (chromium, firefox, webkit); 15.x (chromium) |
|
|
47
|
+
| SvelteKit | 2.x | 2.70.2 (chromium, firefox, webkit) |
|
|
48
|
+
| Nuxt | 3.x (end of life since 31 July 2026: kept working, not officially supported) | 3.21.11 (chromium, firefox, webkit) |
|
|
49
49
|
|
|
50
50
|
Node >=20.19.0; the unit and integration suites run on Node 20, 22, 24, 26. Every version in the table is what the fixture lockfile or the matrix job installs, checked by `npm run compat:check`.
|
|
51
51
|
|
|
52
52
|
Vite 5 through 8: that is what the supported framework majors install (Astro 7 → 8, Astro 6 → 7, Astro 5 → 6, Astro 4 → 5, SvelteKit 2 → 5/6/7/8, Nuxt 3 → 7), measured 2026-09-06. `npm run compat:check` keeps the devDependency and the fixture lockfiles inside that span; `npm run compat:refresh` re-reads it from the registry.
|
|
53
53
|
|
|
54
|
-
The
|
|
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
|
|
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 prerelease 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.
|
|
@@ -64,7 +64,27 @@ The React and Vue hooks and the Next.js and Nuxt fragment renderers run twice on
|
|
|
64
64
|
|
|
65
65
|
<!-- compat-matrix:end -->
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
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
|
+
### Local 2.1 prerelease checks
|
|
70
|
+
|
|
71
|
+
The following checks passed on the 2.1.0 release candidate, the package archive the release pull request's CI built, on 2026-10-18; the Node row on 2026-10-22. 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
|
+
|
|
73
|
+
| Version | Checked population | Passed cases |
|
|
74
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
|
|
75
|
+
| Payload 2.32.3 | DOM, React and Vue, each with frontend-only and serverURL modes | 24 per variant, six variants |
|
|
76
|
+
| Payload 2.32.3 | React and Vue hook lifecycle, frontend-only and serverURL | 9 per variant, four variants |
|
|
77
|
+
| Payload 2.32.3 | React and Vue hook guards, frontend-only and serverURL | 21 per variant, four variants |
|
|
78
|
+
| 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.3.8 | Webpack production preview and fragment rendering | 54 browser cases |
|
|
80
|
+
| Astro 4.0.0 | Base adapter with loader and inline delivery, static output and scoped updates, two control builds | 36 browser cases |
|
|
81
|
+
| Astro 4.16.19 | Native React islands, CSP and navigation ownership | 12 browser cases |
|
|
82
|
+
|
|
83
|
+
The Node row runs the official 20.19.0 binary with no framework or optional peer installed; its fragment endpoint uses a custom renderer and its session check a local `/me` stub, not a real Payload admin, and it covers no framework build, development server or browser. The Next checks use a controlled preview cookie and fragment tokens; they do not use a real Payload admin. The Payload 2 graph still has upstream audit findings, including a critical finding in the 2026-10-05 audit. Its passing preview cases establish compatibility, not a clean dependency graph. Payload 3 lifecycle and Payload 4 canary checks are not included in this table. The Payload 2 lifecycle and guards rows run development builds on Linux engines. The lifecycle row covers an owner replaced while a read is held, a document change by navigation and a reload of the preview frame. The guards row covers a late successful response of a replaced owner, messages from a foreign window and a foreign origin, a receiver switch in a running session and a sign-out in the middle of one; neither row covers a token that expires by time or a production build.
|
|
84
|
+
|
|
85
|
+
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
|
+
|
|
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).
|
|
68
88
|
|
|
69
89
|
## Install
|
|
70
90
|
|
|
@@ -72,11 +92,54 @@ The React and Vue hooks and the Next.js and Nuxt fragment renderers run twice on
|
|
|
72
92
|
npm install payload-live-preview
|
|
73
93
|
```
|
|
74
94
|
|
|
75
|
-
|
|
95
|
+
The root `payload-live-preview` carries the client, the inline script generator, the renderers and four of the five built-in plugins; the unbound-fields overlay and `PluginManager` are on `payload-live-preview/plugins`. `payload-live-preview/plugin` configures Payload's root Live Preview panel, while `/payload` keeps the lower-level URL builder. `payload-live-preview/astro`, `/nextjs`, `/sveltekit` and `/nuxt` hold one framework adapter each; `/nuxt-module` registers the Nuxt one from `nuxt.config.ts` and takes `authorizePreview` by module reference, `authorizePreviewModule` ([docs/nuxt.md](docs/nuxt.md)). `payload-live-preview/server` is the privileged surface for server code: `definePreview()`, `authorizePreviewRequest()`, `issuePreviewToken()` and `createPreviewBindings()`. Every focused entry is listed in [docs/options.md](docs/options.md).
|
|
76
96
|
|
|
77
97
|
## Configure Payload
|
|
78
98
|
|
|
79
|
-
|
|
99
|
+
The config plugin enables mapped collections and globals, installs their shared
|
|
100
|
+
URL callback and can set the toolbar breakpoints. It is structural across
|
|
101
|
+
Payload 2.32.3 and 3.x and imports neither version:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { livePreview } from 'payload-live-preview/plugin';
|
|
105
|
+
|
|
106
|
+
export default buildConfig({
|
|
107
|
+
plugins: [
|
|
108
|
+
livePreview({
|
|
109
|
+
baseUrl: process.env.FRONTEND_URL ?? 'http://localhost:4321',
|
|
110
|
+
collections: {
|
|
111
|
+
posts: ({ data }) => `/blog/${String(data['slug'] ?? '')}`,
|
|
112
|
+
services: ({ data, locale }) => `/${locale ?? 'en'}/services/${String(data['slug'] ?? '')}`,
|
|
113
|
+
},
|
|
114
|
+
globals: {
|
|
115
|
+
homepage: '/',
|
|
116
|
+
},
|
|
117
|
+
breakpoints: [
|
|
118
|
+
{ label: 'Mobile', name: 'mobile', width: 375, height: 667 },
|
|
119
|
+
{ label: 'Desktop', name: 'desktop', width: 1440, height: 900 },
|
|
120
|
+
],
|
|
121
|
+
}),
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Existing root collection/global lists are kept in order and deduplicated with
|
|
127
|
+
the mappings. Existing breakpoints stay when the option is omitted; supplying
|
|
128
|
+
it replaces the list. A competing root URL or a URL on a selected entity that
|
|
129
|
+
already exists when this transform runs is a configuration error. Make this
|
|
130
|
+
transform run after any plugin that may write a Live Preview URL in Payload's
|
|
131
|
+
effective execution order, or compose both routing policies explicitly. The
|
|
132
|
+
complete merge and conflict rules are in [docs/payload.md](docs/payload.md).
|
|
133
|
+
|
|
134
|
+
In the Payload 3.90.2 admin, a collection document gets its native Live
|
|
135
|
+
Preview after its first save. The create screen has no preview toggle and no
|
|
136
|
+
frame, for a function-valued URL and for a fixed string alike. The frame that
|
|
137
|
+
appears after the save loads the URL the plugin's function builds from the saved
|
|
138
|
+
document. `tests/real-payload/surface-create.spec.ts` checks this in Chromium,
|
|
139
|
+
Firefox and WebKit.
|
|
140
|
+
|
|
141
|
+
Use the lower-level callback when another plugin owns the surrounding config or
|
|
142
|
+
when a document may have no preview route:
|
|
80
143
|
|
|
81
144
|
```ts
|
|
82
145
|
import { buildLivePreviewUrl } from 'payload-live-preview/payload';
|
|
@@ -87,8 +150,9 @@ export default buildConfig({
|
|
|
87
150
|
url: buildLivePreviewUrl({
|
|
88
151
|
baseUrl: process.env.FRONTEND_URL ?? 'http://localhost:4321',
|
|
89
152
|
collections: {
|
|
90
|
-
posts: ({ data }) => `/blog/${String(data
|
|
91
|
-
services: ({ data, locale }) =>
|
|
153
|
+
posts: ({ data }) => `/blog/${String(data['slug'] ?? '')}`,
|
|
154
|
+
services: ({ data, locale }) =>
|
|
155
|
+
`/${locale ?? 'en'}/services/${String(data['slug'] ?? '')}`,
|
|
92
156
|
},
|
|
93
157
|
globals: {
|
|
94
158
|
homepage: '/',
|
|
@@ -106,15 +170,20 @@ export default buildConfig({
|
|
|
106
170
|
});
|
|
107
171
|
```
|
|
108
172
|
|
|
109
|
-
The helper appends `?preview=true`, one of the query parameters (`preview`, `draft`, `livePreview`) the adapters read as preview intent. The parameter is client-controlled: it selects delivery and never authorizes draft access. A hand-written `url: ({ data, locale, collectionConfig, globalConfig }) => string` callback works the same way; the [official docs](https://payloadcms.com/docs/live-preview/overview) have the full contract.
|
|
173
|
+
The helper appends `?preview=true`, one of the query parameters (`preview`, `draft`, `livePreview`) the adapters read as preview intent. The parameter is client-controlled: it selects delivery and never authorizes draft access. A hand-written `url: ({ data, locale, collectionConfig, globalConfig }) => string` callback works the same way; the [official docs](https://payloadcms.com/docs/live-preview/overview) have the full contract. The helper's nullable overload lets a resolver return `null` for a document without a route, and `fallback: null` declines every unmapped one; Payload 3 then shows no iframe. Payload 2.32.3's published callback result is string-only, so use the non-null overload and a string fallback there. The config plugin deliberately stays inside that common contract.
|
|
110
174
|
|
|
111
|
-
|
|
175
|
+
The config plugin accepts no signing secret or token option. Where no session
|
|
176
|
+
cookie crosses origins, the manual `signed-token` URL callback is a Payload 3
|
|
177
|
+
server-side integration only; never put its secret in a Payload 2 callback.
|
|
178
|
+
[docs/authorization.md](docs/authorization.md) explains that boundary, the
|
|
179
|
+
open multi-request token lifecycle, the `payload-session` and `verifier`
|
|
180
|
+
strategies, and the initial draft read.
|
|
112
181
|
|
|
113
182
|
## Quick start
|
|
114
183
|
|
|
115
184
|
### Astro
|
|
116
185
|
|
|
117
|
-
```
|
|
186
|
+
```js
|
|
118
187
|
// astro.config.mjs
|
|
119
188
|
import { defineConfig } from 'astro/config';
|
|
120
189
|
import { livePreview } from 'payload-live-preview/astro';
|
|
@@ -143,7 +212,7 @@ Then mark what should update. On a server-rendered page that is one attribute pe
|
|
|
143
212
|
|
|
144
213
|
The attribute needs a renderer behind it: a route exporting `createFragmentEndpoint()` with `authorizePreview` or `authorize`, served at request time (an SSR adapter, `prerender = false` on that route), and its path in the options above as `fragments: { endpoint: '/payload/fragment' }` ([docs/hybrid.md](docs/hybrid.md)). Without `fragments` the bindings inside the boundary are patched instead (`LP0806`), and a boundary with none inside does not update.
|
|
145
214
|
|
|
146
|
-
On a static build there is no server to render it, so the fields are bound individually — and inside a boundary too
|
|
215
|
+
On a static build there is no server to render it, so the fields are bound individually — and inside a boundary too. A direct patch retains the target element. A component render or fragment morph can retain it as well when type, key and position stay compatible, but may replace it when they do not:
|
|
147
216
|
|
|
148
217
|
```astro
|
|
149
218
|
<h1 data-payload-field="title">{title}</h1>
|
|
@@ -153,18 +222,51 @@ On a static build there is no server to render it, so the fields are bound indiv
|
|
|
153
222
|
|
|
154
223
|
Which to reach for, and what each costs: [docs/bindings.md](docs/bindings.md#how-much-markup-this-actually-needs). `pll-codegen annotate` adds the unambiguous bindings and names each one it refuses, with the reason; without `--write` it only reports, and exits with `3` when it found work.
|
|
155
224
|
|
|
156
|
-
That is it: the inline script detects the admin's iframe and starts patching. Rich text is
|
|
225
|
+
That is it: the inline script detects the admin's iframe and starts patching. Rich text is written from the value shape, but a rich-text element that can hold uploads or relationships needs `data-payload-richtext`: only then does the runtime ask Payload to fill those nodes in ([docs/renderers.md](docs/renderers.md#media-urls)). Payload 2.x and 3.x can post raw form values, so relationship and upload fields arrive as IDs until `serverURL` with `mergeDepth` re-fetches the populated document ([docs/options.md](docs/options.md)). Injection modes, request-time middleware, authorization and the initial draft read: [docs/astro.md](docs/astro.md).
|
|
157
226
|
|
|
158
227
|
### Other frameworks
|
|
159
228
|
|
|
160
|
-
- **Next.js (App Router)** —
|
|
229
|
+
- **Next.js (App Router)** — `await LivePreviewScript(…)` in `app/layout.tsx`, the adapter middleware for headers: [docs/nextjs.md](docs/nextjs.md).
|
|
161
230
|
- **SvelteKit** — `livePreviewHandle()` in `hooks.server.ts`: [docs/sveltekit.md](docs/sveltekit.md).
|
|
162
231
|
- **Nuxt** — a Nitro plugin plus a server handler: [docs/nuxt.md](docs/nuxt.md).
|
|
163
232
|
- **Plain HTML** — `generateInlineScript()` at build time: [docs/html.md](docs/html.md).
|
|
164
233
|
|
|
165
234
|
## Patch, fragment, route
|
|
166
235
|
|
|
167
|
-
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
|
|
236
|
+
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).
|
|
237
|
+
|
|
238
|
+
Custom elements are protected from fragment, route and structural morphs by
|
|
239
|
+
default. Add the opt-in attribute `data-payload-morph` to a custom element in
|
|
240
|
+
your template when you want live preview to update its attributes and
|
|
241
|
+
server-rendered light DOM (the children inside the element).
|
|
242
|
+
|
|
243
|
+
```html
|
|
244
|
+
<my-component data-payload-morph>
|
|
245
|
+
<!-- Server-rendered content that live preview can update -->
|
|
246
|
+
</my-component>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The attribute must appear on the element already on the preview page and in
|
|
250
|
+
the updated HTML returned by your template. Adding it to the template normally
|
|
251
|
+
covers both. The attribute opts in when present; its value is ignored.
|
|
252
|
+
Compatible hosts with the same tag and namespace retain their identity,
|
|
253
|
+
instance properties and listeners while attributes and light DOM update.
|
|
254
|
+
If only one version has the attribute, or the elements are incompatible, the
|
|
255
|
+
existing replacement behavior applies.
|
|
256
|
+
Shadow roots are never touched; islands, `data-payload-owned`, editable content
|
|
257
|
+
and nested protected descendants remain protected. Ordinary elements are unchanged.
|
|
258
|
+
|
|
259
|
+
The morph makes the host's attributes and light DOM match the server's markup,
|
|
260
|
+
so what a component added to its own host or built inside it is removed or
|
|
261
|
+
replaced on the next update (the state attributes `open`, `value`, `checked` and
|
|
262
|
+
`selected` are left alone unless the template names them). Keep markup a
|
|
263
|
+
component owns in its shadow root, or mark it `data-payload-owned`. Components
|
|
264
|
+
must react to child changes, refresh cached references, preserve UI state, and
|
|
265
|
+
clean up listeners or observers. `connectedCallback` does not rerun for child
|
|
266
|
+
updates; DOM moves can still reconnect a retained host. Consumers may emit the
|
|
267
|
+
marker only in preview mode on initial and subsequent renders; preview
|
|
268
|
+
detection stays in the application. See
|
|
269
|
+
[ADR 0008](docs/architecture/0008-keyed-morph-ownership.md#10-custom-element-light-dom-opt-in-2026-10-10).
|
|
168
270
|
|
|
169
271
|
## Events and plugins
|
|
170
272
|
|
|
@@ -182,9 +284,9 @@ Every event, transforms, custom field renderers, the built-in plugins and the pl
|
|
|
182
284
|
|
|
183
285
|
## Security model
|
|
184
286
|
|
|
185
|
-
- **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
|
|
186
|
-
- **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). The adapters merge a `frame-ancestors` policy for the admin origins without clobbering the rest of your CSP.
|
|
187
|
-
- **Sanitization and URL validation.** Lexical and HTML writes 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"`.
|
|
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 `defaults: 'v1'` (or `strict: false`) injects on intent alone.
|
|
288
|
+
- **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
|
+
- **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)).
|
|
188
290
|
- **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-*`.
|
|
189
291
|
- **Policed writes, no prototype pollution.** `data-payload-attribute` refuses event handlers, `style`, `srcdoc`, `formaction`, `form`, `id`, `name`, `is`, `srcset` and `imagesrcset`; nested lookups refuse `__proto__`, `prototype` and `constructor`, and incoming data is never merged into existing objects.
|
|
190
292
|
|
|
@@ -194,8 +296,8 @@ Full details in [docs/security.md](docs/security.md). Report vulnerabilities per
|
|
|
194
296
|
|
|
195
297
|
- **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).
|
|
196
298
|
- **Relationship fields show IDs.** Set `serverURL` with `mergeDepth`; Payload 3.x posts unpopulated form values.
|
|
197
|
-
- **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; an `X-Frame-Options: DENY` from a proxy must go.
|
|
198
|
-
- **The adapter refuses
|
|
299
|
+
- **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.
|
|
199
301
|
|
|
200
302
|
`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=…"`.
|
|
201
303
|
|
|
@@ -205,6 +307,7 @@ The reading path, with a glossary: [docs/README.md](docs/README.md).
|
|
|
205
307
|
|
|
206
308
|
- Framework guides: [Astro](docs/astro.md) · [Next.js](docs/nextjs.md) · [SvelteKit](docs/sveltekit.md) · [Nuxt](docs/nuxt.md) · [Plain HTML](docs/html.md) · [React hook](docs/react.md) · [Vue composable](docs/vue.md)
|
|
207
309
|
- [docs/bindings.md](docs/bindings.md) — data attributes, field types, owners, typed bindings and codegen
|
|
310
|
+
- [docs/payload.md](docs/payload.md) — Payload 2/3 config plugin, merge rules and URL conflicts
|
|
208
311
|
- [docs/options.md](docs/options.md) — package entries, every option and its default, Payload 3.x population
|
|
209
312
|
- [docs/authorization.md](docs/authorization.md) — strategies, signed tokens, the initial draft read
|
|
210
313
|
- [docs/hybrid.md](docs/hybrid.md) — patch, fragment and route
|
|
@@ -212,7 +315,7 @@ The reading path, with a glossary: [docs/README.md](docs/README.md).
|
|
|
212
315
|
- [docs/deployment.md](docs/deployment.md) — CSP, caches, proxies
|
|
213
316
|
- [docs/troubleshooting.md](docs/troubleshooting.md) — `inspect()`, `pll doctor`, diagnostic codes
|
|
214
317
|
- [docs/security.md](docs/security.md) · [docs/migration.md](docs/migration.md) · [docs/interop.md](docs/interop.md) · [docs/reveal.md](docs/reveal.md)
|
|
215
|
-
- Maintainers: [docs/testing.md](docs/testing.md), [docs/benchmarks.md](docs/benchmarks.md), [architecture decisions](docs/architecture/README.md), [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
318
|
+
- Maintainers: [docs/testing.md](docs/testing.md), [docs/benchmarks.md](docs/benchmarks.md), [docs/audit.md](docs/audit.md), [architecture decisions](docs/architecture/README.md), [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
216
319
|
|
|
217
320
|
## License
|
|
218
321
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
/** The consumer's Astro compiler owns this internal template, not a private factory.
|
|
3
|
+
* Fresh container locals carry only the registry-selected component and its props.
|
|
4
|
+
*/
|
|
5
|
+
import type { AstroInstance } from 'astro';
|
|
6
|
+
|
|
7
|
+
type BridgeData = {
|
|
8
|
+
component: AstroInstance['default'];
|
|
9
|
+
props: Record<string, unknown>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function isBridgeData(value: unknown): value is BridgeData {
|
|
13
|
+
return (
|
|
14
|
+
typeof value === 'object' && value !== null &&
|
|
15
|
+
'component' in value && typeof value.component === 'function' &&
|
|
16
|
+
'props' in value && typeof value.props === 'object' && value.props !== null &&
|
|
17
|
+
!Array.isArray(value.props)
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const bridge: unknown = Reflect.get(Astro.locals, '__payloadLivePreviewFragment');
|
|
22
|
+
if (!isBridgeData(bridge)) throw new Error('Missing trusted fragment bridge');
|
|
23
|
+
const Component = bridge.component;
|
|
24
|
+
---
|
|
25
|
+
<Component {...bridge.props} />
|
|
@@ -7,10 +7,8 @@
|
|
|
7
7
|
* see when they fill a previously empty field. `PreviewBoundary` renders the
|
|
8
8
|
* wrapper always: with content when the value is present, and as an empty,
|
|
9
9
|
* `hidden` element otherwise — out of layout and out of the accessibility
|
|
10
|
-
* tree
|
|
11
|
-
*
|
|
12
|
-
* runtime removes `hidden`; when it empties it, the runtime hides the
|
|
13
|
-
* wrapper again.
|
|
10
|
+
* tree. When an update fills the field, the runtime removes `hidden`; when
|
|
11
|
+
* it empties it, the runtime hides the wrapper again.
|
|
14
12
|
*
|
|
15
13
|
* Usage:
|
|
16
14
|
*
|
|
@@ -32,12 +32,13 @@
|
|
|
32
32
|
* Their output is yours to escape here. The built-in renderers escape
|
|
33
33
|
* every value they interpolate, but the sanitizer that would catch a
|
|
34
34
|
* custom renderer's mistake needs a DOM, and there is none during SSR
|
|
35
|
-
* unless the
|
|
36
|
-
*
|
|
35
|
+
* unless the page passes one in `document`. With `requireDocument` the
|
|
36
|
+
* render throws instead of returning unsanitised markup, as 3.0 will by
|
|
37
|
+
* default (ADR 0025). See "HTML sanitization" in docs/security.md.
|
|
37
38
|
*/
|
|
38
39
|
// Self-referencing import so the shipped copy in dist/ resolves
|
|
39
40
|
// through the package exports map in consumer projects.
|
|
40
|
-
import { isLexicalContent, lexicalToHtml } from 'payload-live-preview';
|
|
41
|
+
import { isLexicalContent, lexicalToHtml, type SanitizerDocument } from 'payload-live-preview';
|
|
41
42
|
|
|
42
43
|
interface Props {
|
|
43
44
|
/** The Lexical value from Payload (may be null/undefined/empty). */
|
|
@@ -50,11 +51,31 @@ interface Props {
|
|
|
50
51
|
/** Wrapper element. Default `div`. */
|
|
51
52
|
as?: string;
|
|
52
53
|
class?: string;
|
|
54
|
+
/**
|
|
55
|
+
* The DOM the sanitizer parses with on the server, e.g. linkedom's
|
|
56
|
+
* `parseHTML(...).document`, for this render. Without it the document
|
|
57
|
+
* `setSanitizerDocument()` supplied is used.
|
|
58
|
+
*/
|
|
59
|
+
document?: SanitizerDocument;
|
|
60
|
+
/** Throw instead of rendering unsanitised markup when no document is available (ADR 0025). */
|
|
61
|
+
requireDocument?: boolean;
|
|
53
62
|
}
|
|
54
63
|
|
|
55
|
-
const {
|
|
64
|
+
const {
|
|
65
|
+
value,
|
|
66
|
+
field,
|
|
67
|
+
as: tag = 'div',
|
|
68
|
+
class: className,
|
|
69
|
+
document: sanitizerDocument,
|
|
70
|
+
requireDocument,
|
|
71
|
+
} = Astro.props;
|
|
56
72
|
const Tag = tag;
|
|
57
|
-
const html = isLexicalContent(value)
|
|
73
|
+
const html = isLexicalContent(value)
|
|
74
|
+
? lexicalToHtml(value, {
|
|
75
|
+
...(sanitizerDocument === undefined ? {} : { document: sanitizerDocument }),
|
|
76
|
+
...(requireDocument === undefined ? {} : { requireDocument }),
|
|
77
|
+
})
|
|
78
|
+
: '';
|
|
58
79
|
---
|
|
59
80
|
|
|
60
81
|
<Tag
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { L as LivePreviewAstroOptions } from '../../middleware-
|
|
2
|
-
export { a as LivePreviewMiddleware, c as createLivePreviewMiddleware } from '../../middleware-
|
|
3
|
-
import { h as hasPreviewIntent } from '../../options-
|
|
4
|
-
export { P as PreviewAdapterOptions, a as PreviewRequestLike, b as PreviewRequestOptions, c as PreviewSignal } from '../../options-
|
|
5
|
-
import { F as FragmentEndpointOptions$1, a as FragmentRegistry$1, b as FragmentRegistryEntry$1, c as FragmentRenderer$1 } from '../../fragment-endpoint-
|
|
6
|
-
export { d as FragmentRenderInput } from '../../fragment-endpoint-
|
|
7
|
-
export { A as AUTHORIZATION_LOCALS_KEY, a as AUTHORIZATION_OUTCOME_LOCALS_KEY, L as LivePreviewLocals, N as NONCE_LOCALS_KEY } from '../../locals-
|
|
1
|
+
import { L as LivePreviewAstroOptions } from '../../middleware-BGobLN9P.js';
|
|
2
|
+
export { a as LivePreviewMiddleware, c as createLivePreviewMiddleware } from '../../middleware-BGobLN9P.js';
|
|
3
|
+
import { h as hasPreviewIntent } from '../../options-Cm8fC7dj.js';
|
|
4
|
+
export { P as PreviewAdapterOptions, a as PreviewRequestLike, b as PreviewRequestOptions, c as PreviewSignal } from '../../options-Cm8fC7dj.js';
|
|
5
|
+
import { F as FragmentEndpointOptions$1, a as FragmentRegistry$1, b as FragmentRegistryEntry$1, c as FragmentRenderer$1 } from '../../fragment-endpoint-B2qcRw5T.js';
|
|
6
|
+
export { d as FragmentRenderInput } from '../../fragment-endpoint-B2qcRw5T.js';
|
|
7
|
+
export { A as AUTHORIZATION_LOCALS_KEY, a as AUTHORIZATION_OUTCOME_LOCALS_KEY, L as LivePreviewLocals, N as NONCE_LOCALS_KEY } from '../../locals-B1mF3aTb.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* The Astro integration for `astro.config.mjs`: `livePreview({ ... })`.
|
|
11
11
|
* Inline and loader modes deliver at build time; middleware mode registers
|
|
12
|
-
* the request-time middleware through a serialized options module.
|
|
12
|
+
* the request-time middleware through a serialized options module. Every
|
|
13
|
+
* build also records its island module table for fragment containers (ADR 0021).
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
type ScriptStage = 'head-inline' | 'page' | 'before-hydration' | 'page-ssr';
|
|
@@ -60,11 +61,41 @@ interface AstroIntegrationLike {
|
|
|
60
61
|
readonly name: string;
|
|
61
62
|
readonly hooks: {
|
|
62
63
|
readonly 'astro:config:setup': (ctx: AstroConfigSetupContext) => void;
|
|
64
|
+
readonly 'astro:config:done': (ctx: {
|
|
65
|
+
readonly config: {
|
|
66
|
+
readonly output?: string;
|
|
67
|
+
readonly build?: {
|
|
68
|
+
readonly server?: URL;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
readonly buildOutput?: string;
|
|
72
|
+
}) => void;
|
|
73
|
+
readonly 'astro:build:ssr': (ctx: {
|
|
74
|
+
readonly manifest: {
|
|
75
|
+
readonly entryModules?: Readonly<Record<string, string>>;
|
|
76
|
+
readonly base?: string;
|
|
77
|
+
readonly assetsPrefix?: string | Readonly<Record<string, string>>;
|
|
78
|
+
};
|
|
79
|
+
}) => void;
|
|
80
|
+
readonly 'astro:build:done': (ctx: {
|
|
81
|
+
readonly logger?: {
|
|
82
|
+
readonly info: (message: string) => void;
|
|
83
|
+
};
|
|
84
|
+
}) => Promise<void>;
|
|
63
85
|
};
|
|
64
86
|
}
|
|
65
87
|
/** Build the integration. The injected runtime stays inert outside the admin's preview iframe. */
|
|
66
88
|
declare function livePreview(options?: LivePreviewAstroOptions): AstroIntegrationLike;
|
|
67
89
|
|
|
90
|
+
/** Resolves an island's component, renderer and before-hydration specifiers. */
|
|
91
|
+
type IslandModuleResolver = (specifier: string) => Promise<string>;
|
|
92
|
+
/**
|
|
93
|
+
* Pass as the container's `resolve`, next to the framework's server and client
|
|
94
|
+
* renderers: `AstroContainer.create({ resolve: resolveIslandModule })`.
|
|
95
|
+
* Needs Astro 4.16 or later, whose container accepts `resolve`.
|
|
96
|
+
*/
|
|
97
|
+
declare const resolveIslandModule: IslandModuleResolver;
|
|
98
|
+
|
|
68
99
|
/**
|
|
69
100
|
* Manual embedding for `.astro` templates, for the cases the integration's
|
|
70
101
|
* automatic injection does not fit (one layout, a conditional).
|
|
@@ -108,6 +139,7 @@ type FragmentEndpointOptions = FragmentEndpointOptions$1<AstroComponentLike>;
|
|
|
108
139
|
/** Build the endpoint; export it as the `POST` of a non-prerendered Astro API route. */
|
|
109
140
|
declare function createFragmentEndpoint(options: FragmentEndpointOptions): (context: {
|
|
110
141
|
readonly request: Request;
|
|
142
|
+
readonly locals?: unknown;
|
|
111
143
|
}) => Promise<Response>;
|
|
112
144
|
|
|
113
|
-
export { type AstroComponentLike, type AstroIntegrationLike, type FragmentEndpointOptions, type FragmentRegistry, type FragmentRegistryEntry, type FragmentRenderer, LivePreviewAstroOptions, type RenderScriptOptions, createFragmentEndpoint, hasPreviewIntent, isPreviewRequest, livePreview, renderLivePreviewScript };
|
|
145
|
+
export { type AstroComponentLike, type AstroIntegrationLike, type FragmentEndpointOptions, type FragmentRegistry, type FragmentRegistryEntry, type FragmentRenderer, type IslandModuleResolver, LivePreviewAstroOptions, type RenderScriptOptions, createFragmentEndpoint, hasPreviewIntent, isPreviewRequest, livePreview, renderLivePreviewScript, resolveIslandModule };
|