payload-live-preview 1.8.0 → 2.0.0-beta.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 +301 -93
- package/dist/adapters/astro/PreviewBoundary.astro +66 -0
- package/dist/adapters/astro/RichText.astro +6 -0
- package/dist/adapters/astro/index.d.ts +165 -134
- 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 +4 -9
- 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 +13 -80
- package/dist/adapters/nextjs/index.js +1 -1
- package/dist/adapters/nextjs/index.js.map +1 -1
- package/dist/adapters/nuxt/index.d.ts +46 -106
- package/dist/adapters/nuxt/index.js +1 -1
- package/dist/adapters/nuxt/index.js.map +1 -1
- package/dist/adapters/sveltekit/index.d.ts +10 -75
- package/dist/adapters/sveltekit/index.js +1 -1
- package/dist/adapters/sveltekit/index.js.map +1 -1
- package/dist/client.cjs +2 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +747 -0
- package/dist/client.d.ts +747 -0
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -0
- package/dist/codegen-astro.d.ts +5 -27
- package/dist/codegen-astro.js +1 -1
- package/dist/codegen-astro.js.map +1 -1
- package/dist/codegen-cli.d.ts +3 -16
- package/dist/codegen-cli.js +1 -1
- package/dist/codegen-cli.js.map +1 -1
- package/dist/codegen.cjs +1 -1
- package/dist/codegen.cjs.map +1 -1
- package/dist/codegen.d.cts +37 -124
- package/dist/codegen.d.ts +37 -124
- package/dist/codegen.js +1 -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 +647 -1100
- package/dist/core.d.ts +647 -1100
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/doctor-cli.d.ts +3 -28
- package/dist/doctor-cli.js +1 -1
- package/dist/doctor-cli.js.map +1 -1
- package/dist/doctor.d.ts +16 -32
- package/dist/doctor.js +1 -1
- package/dist/doctor.js.map +1 -1
- package/dist/fragment.cjs +2 -0
- package/dist/fragment.cjs.map +1 -0
- package/dist/fragment.d.cts +333 -0
- package/dist/fragment.d.ts +333 -0
- package/dist/fragment.js +2 -0
- package/dist/fragment.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +885 -1531
- package/dist/index.d.ts +885 -1531
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lexical.cjs +2 -0
- package/dist/lexical.cjs.map +1 -0
- package/dist/lexical.d.cts +90 -0
- package/dist/lexical.d.ts +90 -0
- package/dist/lexical.js +2 -0
- package/dist/lexical.js.map +1 -0
- package/dist/middleware-CEPsY1P7.d.ts +40 -0
- package/dist/migrate.d.ts +73 -0
- package/dist/migrate.js +2 -0
- package/dist/migrate.js.map +1 -0
- package/dist/options-CEzzfHJu.d.ts +197 -0
- package/dist/payload.cjs +1 -1
- package/dist/payload.cjs.map +1 -1
- package/dist/payload.d.cts +11 -80
- package/dist/payload.d.ts +11 -80
- package/dist/payload.js +1 -1
- package/dist/payload.js.map +1 -1
- package/dist/plugins.cjs +2 -0
- package/dist/plugins.cjs.map +1 -0
- package/dist/plugins.d.cts +509 -0
- package/dist/plugins.d.ts +509 -0
- package/dist/plugins.js +2 -0
- package/dist/plugins.js.map +1 -0
- package/dist/{probe-tMHdim5y.d.ts → probe-CYtvj_Lm.d.ts} +43 -77
- package/dist/server.cjs +2 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +435 -0
- package/dist/server.d.ts +435 -0
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -0
- package/dist/structural.cjs +3 -0
- package/dist/structural.cjs.map +1 -0
- package/dist/structural.d.cts +243 -0
- package/dist/structural.d.ts +243 -0
- package/dist/structural.js +3 -0
- package/dist/structural.js.map +1 -0
- package/package.json +86 -7
- package/dist/middleware-DD4Hwomt.d.ts +0 -189
package/dist/index.d.ts
CHANGED
|
@@ -1,67 +1,22 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Single source of truth for the library version.
|
|
3
|
-
*
|
|
4
|
-
* Both the npm package surface (`src/index.ts`, `src/core-entry.ts`)
|
|
5
|
-
* and the inline runtime (`src/core/runtime.ts`, which gets bundled by
|
|
6
|
-
* esbuild and embedded into `runtime.generated.ts`) read the version
|
|
7
|
-
* from here so they never drift.
|
|
8
|
-
*
|
|
9
|
-
* Imports `package.json` directly — tsc, tsup, and esbuild all inline
|
|
10
|
-
* the value at build time when `resolveJsonModule` is enabled.
|
|
11
|
-
*
|
|
12
|
-
* @module @/version
|
|
13
|
-
*/
|
|
1
|
+
/** The package version, read from package.json so the entries and the inline runtime cannot drift. */
|
|
14
2
|
declare const VERSION: string;
|
|
15
3
|
|
|
16
|
-
/**
|
|
17
|
-
* Connection state + heartbeat.
|
|
18
|
-
*
|
|
19
|
-
* Tracks whether the runtime is currently receiving updates from a
|
|
20
|
-
* trusted origin and fires a callback when the heartbeat times out.
|
|
21
|
-
*
|
|
22
|
-
* The heartbeat is reset on every valid message and considered dead
|
|
23
|
-
* after `timeoutMs` of silence. The host typically responds by
|
|
24
|
-
* re-sending the `ready` handshake to attempt reconnection.
|
|
25
|
-
*
|
|
26
|
-
* @module @core/state
|
|
27
|
-
*/
|
|
4
|
+
/** Connection status and the heartbeat that decides a disconnect. */
|
|
28
5
|
type ConnectionStatus = 'disconnected' | 'connecting' | 'connected';
|
|
29
6
|
|
|
30
7
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* internal state the runtime does not otherwise expose.
|
|
37
|
-
*
|
|
38
|
-
* A snapshot is a read of what the runtime already holds. It discloses nothing
|
|
39
|
-
* that is not already observable on the page: the trusted origins are inside
|
|
40
|
-
* the injected script, the field names are `data-payload-field` attributes in
|
|
41
|
-
* the DOM, and the counters are derived from those. It is therefore available
|
|
42
|
-
* in production rather than gated to development builds — a preview that
|
|
43
|
-
* misbehaves only on the deployed site is exactly the case where the
|
|
44
|
-
* information is worth having, and a development-only gate would withhold it
|
|
45
|
-
* there.
|
|
46
|
-
*
|
|
47
|
-
* Nothing here is transmitted anywhere. Reading a snapshot performs no I/O.
|
|
48
|
-
*
|
|
49
|
-
* @module @core/inspection/types
|
|
8
|
+
* Shape of the `inspect()` snapshot. It only re-reads what the page already
|
|
9
|
+
* shows — origins from the injected script, field names from the DOM,
|
|
10
|
+
* counters derived from both — so it stays available in production, where a
|
|
11
|
+
* misbehaving preview is exactly the case worth diagnosing. Reading one
|
|
12
|
+
* performs no I/O and transmits nothing.
|
|
50
13
|
*/
|
|
51
14
|
|
|
52
15
|
/** Origin trust as the runtime currently sees it. */
|
|
53
16
|
interface InspectionOrigins {
|
|
54
|
-
/**
|
|
55
|
-
* Origins the runtime is willing to accept messages from, as configured.
|
|
56
|
-
* An empty list means no explicit allow-list was given, which is the
|
|
57
|
-
* condition under which referrer and localhost heuristics decide instead.
|
|
58
|
-
*/
|
|
17
|
+
/** Configured origins; empty means the referrer and localhost heuristics decide instead. */
|
|
59
18
|
readonly trusted: readonly string[];
|
|
60
|
-
/**
|
|
61
|
-
* The origin the runtime locked onto after its first accepted update, or
|
|
62
|
-
* `undefined` while still unlocked. Once locked, every other origin is
|
|
63
|
-
* refused for the rest of the session.
|
|
64
|
-
*/
|
|
19
|
+
/** The origin locked onto by the first accepted update; every other is then refused. */
|
|
65
20
|
readonly locked: string | undefined;
|
|
66
21
|
}
|
|
67
22
|
/** Protocol versions and the capabilities they enable. */
|
|
@@ -74,6 +29,10 @@ interface InspectionProtocol {
|
|
|
74
29
|
readonly negotiated: number;
|
|
75
30
|
/** Capability flags enabled at the negotiated version. */
|
|
76
31
|
readonly capabilities: readonly string[];
|
|
32
|
+
/** The subset of `capabilities` seen on the wire rather than granted by version. */
|
|
33
|
+
readonly observed: readonly string[];
|
|
34
|
+
/** The Payload profile the observed capabilities imply: `unknown`, `payload-2` or `payload-3`. */
|
|
35
|
+
readonly profile: string;
|
|
77
36
|
}
|
|
78
37
|
/** Revision accounting for the update pipeline. */
|
|
79
38
|
interface InspectionRevisions {
|
|
@@ -86,6 +45,19 @@ interface InspectionRevisions {
|
|
|
86
45
|
* when the last update is among the abandoned ones.
|
|
87
46
|
*/
|
|
88
47
|
readonly superseded: number;
|
|
48
|
+
/**
|
|
49
|
+
* Updates whose scheduled writes reached the DOM. What is neither
|
|
50
|
+
* superseded nor completed is in flight or was cancelled; a `superseded`
|
|
51
|
+
* that tracks `accepted` while `completed` stays low is the editor
|
|
52
|
+
* out-typing the pipeline.
|
|
53
|
+
*/
|
|
54
|
+
readonly completed: number;
|
|
55
|
+
/**
|
|
56
|
+
* Bindings not scheduled because their value was identical to the one last
|
|
57
|
+
* applied. Cumulative since start; always `0` unless `skipUnchanged` is on.
|
|
58
|
+
* A large number beside a small `accepted` is the optimisation working.
|
|
59
|
+
*/
|
|
60
|
+
readonly skippedUnchanged: number;
|
|
89
61
|
/** Revision currently in flight, or `undefined` when the pipeline is idle. */
|
|
90
62
|
readonly active: number | undefined;
|
|
91
63
|
}
|
|
@@ -98,22 +70,14 @@ interface InspectionBindings {
|
|
|
98
70
|
/** Those field names, sorted. */
|
|
99
71
|
readonly fieldNames: readonly string[];
|
|
100
72
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* to update: a name here is a markup problem, a name absent from here and
|
|
104
|
-
* from `fieldNames` was never sent.
|
|
73
|
+
* Bound fields some update carried no value for, so the binding kept its old
|
|
74
|
+
* text — why a binding can stay stale while its siblings update. Cumulative.
|
|
105
75
|
*/
|
|
76
|
+
readonly absentFields: readonly string[];
|
|
106
77
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* `orphanFields`, and the exact opposite of it: a binding with no value
|
|
110
|
-
* rather than a value with no binding.
|
|
111
|
-
*
|
|
112
|
-
* This is why a binding can stay stale while its siblings update. Without
|
|
113
|
-
* it the two cases are indistinguishable from the DOM, since neither
|
|
114
|
-
* leaves a trace.
|
|
78
|
+
* Field names that arrived but matched no binding — a markup problem.
|
|
79
|
+
* A name in neither this list nor `fieldNames` was never sent. Cumulative.
|
|
115
80
|
*/
|
|
116
|
-
readonly absentFields: readonly string[];
|
|
117
81
|
readonly orphanFields: readonly string[];
|
|
118
82
|
/** Whether `scopeBindingsByOwner` is active. */
|
|
119
83
|
readonly ownerScoped: boolean;
|
|
@@ -124,11 +88,7 @@ interface InspectionBindings {
|
|
|
124
88
|
interface InspectionScheduler {
|
|
125
89
|
/** Writes buffered for the next flush. */
|
|
126
90
|
readonly pending: number;
|
|
127
|
-
/**
|
|
128
|
-
* Writes held back because their element is offscreen and the binding count
|
|
129
|
-
* is above `visibilityGateThreshold`. These are applied when the element
|
|
130
|
-
* scrolls into view — never, on a page nobody scrolls.
|
|
131
|
-
*/
|
|
91
|
+
/** Writes held back offscreen above `visibilityGateThreshold`; applied when scrolled into view. */
|
|
132
92
|
readonly deferred: number;
|
|
133
93
|
/** The binding count above which the visibility gate starts deferring. */
|
|
134
94
|
readonly visibilityGateThreshold: number;
|
|
@@ -137,23 +97,13 @@ interface InspectionScheduler {
|
|
|
137
97
|
/** Stats of the most recent flush, or `undefined` if none has run. */
|
|
138
98
|
readonly lastFlush: {
|
|
139
99
|
readonly applied: number;
|
|
140
|
-
/**
|
|
141
|
-
* Field names the flush applied, in application order.
|
|
142
|
-
*
|
|
143
|
-
* `applied` is a count, and a count cannot separate "this binding was
|
|
144
|
-
* written" from "this binding was never scheduled" — a stale binding
|
|
145
|
-
* next to a non-zero count is consistent with both. The names can.
|
|
146
|
-
*/
|
|
100
|
+
/** Which fields the flush wrote; a count alone cannot say a binding was skipped. */
|
|
147
101
|
readonly appliedFields: readonly string[];
|
|
148
102
|
readonly deferred: number;
|
|
149
103
|
readonly durationMs: number;
|
|
150
104
|
} | undefined;
|
|
151
105
|
}
|
|
152
|
-
/**
|
|
153
|
-
* A point-in-time read of runtime state, for diagnosing a preview that is not
|
|
154
|
-
* behaving. Every field is a plain value; the snapshot holds no references
|
|
155
|
-
* into runtime internals and does not change after it is returned.
|
|
156
|
-
*/
|
|
106
|
+
/** A point-in-time read; plain values only, with no references into runtime internals. */
|
|
157
107
|
interface LivePreviewInspection {
|
|
158
108
|
/** Package version that produced this snapshot. */
|
|
159
109
|
readonly version: string;
|
|
@@ -168,65 +118,67 @@ interface LivePreviewInspection {
|
|
|
168
118
|
readonly scheduler: InspectionScheduler;
|
|
169
119
|
/** Field types with a registered renderer, sorted. */
|
|
170
120
|
readonly renderers: readonly string[];
|
|
121
|
+
/** Registered plugins and their live registrations; empty on a bare runtime. */
|
|
122
|
+
readonly plugins: readonly PluginInspection[];
|
|
123
|
+
/** Server-rendered fragment boundaries: whether a handler exists and what happened to renders. */
|
|
124
|
+
readonly fragments: InspectionFragments;
|
|
125
|
+
/** Route refreshes: whether a strategy exists, how many ran, failed, or were stopped by the loop guard. */
|
|
126
|
+
readonly route: InspectionRoute;
|
|
127
|
+
}
|
|
128
|
+
/** The route strategy as `inspect()` reports it. */
|
|
129
|
+
interface InspectionRoute {
|
|
130
|
+
readonly handler: boolean;
|
|
131
|
+
readonly refreshes: number;
|
|
132
|
+
readonly failed: number;
|
|
133
|
+
/** Second refresh requests for one revision, refused with LP0805. */
|
|
134
|
+
readonly loopStopped: number;
|
|
135
|
+
}
|
|
136
|
+
/** The fragment strategy as `inspect()` reports it. */
|
|
137
|
+
interface InspectionFragments {
|
|
138
|
+
/** Whether a fragment handler is configured; without one, boundaries are patched. */
|
|
139
|
+
readonly handler: boolean;
|
|
140
|
+
readonly inFlight: number;
|
|
141
|
+
readonly rendered: number;
|
|
142
|
+
readonly failed: number;
|
|
143
|
+
readonly superseded: number;
|
|
144
|
+
}
|
|
145
|
+
/** One plugin as `inspect().plugins` reports it. */
|
|
146
|
+
interface PluginInspection {
|
|
147
|
+
readonly name: string;
|
|
148
|
+
readonly version: string | undefined;
|
|
149
|
+
/** `initializing` while `init` runs, `active` afterwards, `tearing-down` while `destroy` runs. */
|
|
150
|
+
readonly state: 'initializing' | 'active' | 'tearing-down';
|
|
151
|
+
/** Live registrations owned by the plugin's scope, by kind. */
|
|
152
|
+
readonly registrations: {
|
|
153
|
+
readonly transforms: number;
|
|
154
|
+
readonly renderers: number;
|
|
155
|
+
readonly subscriptions: number;
|
|
156
|
+
readonly cleanups: number;
|
|
157
|
+
};
|
|
171
158
|
}
|
|
172
159
|
|
|
173
160
|
/**
|
|
174
|
-
* Payload
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* `window.postMessage`, verified against the shipped admin code
|
|
178
|
-
* (`@payloadcms/ui` → `elements/LivePreview/Window`):
|
|
179
|
-
*
|
|
180
|
-
* - Payload **3.x** sends `{ type, collectionSlug, globalSlug, data,
|
|
181
|
-
* locale, externallyUpdatedRelationship }` on every form edit —
|
|
182
|
-
* `data` holds **raw form values** (relationships as bare IDs) and
|
|
183
|
-
* there is **no** `fieldSchemaJSON`.
|
|
184
|
-
* - Payload **2.x** additionally sent `fieldSchemaJSON` with the
|
|
185
|
-
* first update (client-side merge era).
|
|
186
|
-
* - `previewToken` and `protocolVersion` are extensions of THIS
|
|
187
|
-
* library for custom admin senders; stock Payload never sends
|
|
188
|
-
* them and ignores them in our `ready` handshake.
|
|
189
|
-
*
|
|
190
|
-
* @module @/types/payload-protocol
|
|
191
|
-
*/
|
|
192
|
-
/**
|
|
193
|
-
* A `payload-live-preview` data update.
|
|
194
|
-
*
|
|
195
|
-
* `data` is the serialized document state. `fieldSchemaJSON`, when
|
|
196
|
-
* present (Payload 2.x or custom senders), allows the schema-driven
|
|
197
|
-
* engine to resolve field types without any DOM annotations.
|
|
161
|
+
* Payload's live-preview `postMessage` protocol as the shipped admin sends it:
|
|
162
|
+
* raw form values on every edit, no schema. `previewToken` and
|
|
163
|
+
* `protocolVersion` are extensions of this library for custom senders.
|
|
198
164
|
*/
|
|
199
165
|
interface PayloadLivePreviewMessage {
|
|
200
166
|
readonly type: 'payload-live-preview';
|
|
201
167
|
readonly data?: Record<string, unknown>;
|
|
168
|
+
/** Sent by Payload 2.x and custom senders only. */
|
|
202
169
|
readonly fieldSchemaJSON?: readonly PayloadFieldSchema[];
|
|
203
170
|
readonly globalSlug?: string;
|
|
204
171
|
readonly collectionSlug?: string;
|
|
205
172
|
readonly locale?: string;
|
|
206
173
|
readonly ready?: boolean;
|
|
207
|
-
/**
|
|
208
|
-
* Payload 3.x: most recent relationship-document event (create/update
|
|
209
|
-
* in a drawer), or `null`. The official client ignores it too — it is
|
|
210
|
-
* modeled here for completeness and consumer event access.
|
|
211
|
-
*/
|
|
174
|
+
/** Payload 3.x: the most recent relationship-document event (a drawer save), or `null`. */
|
|
212
175
|
readonly externallyUpdatedRelationship?: PayloadDocumentEventDetail | null;
|
|
213
|
-
/**
|
|
214
|
-
* Optional preview JWT. ⚠️ Library extension — stock Payload never
|
|
215
|
-
* sends one. When `RuntimeOptions.validateToken` is set, the runtime
|
|
216
|
-
* requires every data update to carry a token that passes the
|
|
217
|
-
* validator; messages without a valid token are dropped.
|
|
218
|
-
*/
|
|
176
|
+
/** Library extension: the token `validateToken` checks. Stock Payload never sends one. */
|
|
219
177
|
readonly previewToken?: string;
|
|
220
|
-
/**
|
|
221
|
-
* Highest protocol version the sender supports. ⚠️ Library extension
|
|
222
|
-
* for custom senders — stock Payload sends no version. When absent,
|
|
223
|
-
* the receiver assumes v1.
|
|
224
|
-
*/
|
|
178
|
+
/** Library extension: the highest protocol version the sender supports; absent means v1. */
|
|
225
179
|
readonly protocolVersion?: number;
|
|
226
180
|
}
|
|
227
|
-
/**
|
|
228
|
-
* Payload's `DocumentEvent` shape (admin `providers/DocumentEvents`).
|
|
229
|
-
*/
|
|
181
|
+
/** Payload's `DocumentEvent` shape (admin `providers/DocumentEvents`). */
|
|
230
182
|
interface PayloadDocumentEventDetail {
|
|
231
183
|
readonly entitySlug: string;
|
|
232
184
|
readonly operation?: 'create' | 'update';
|
|
@@ -234,9 +186,7 @@ interface PayloadDocumentEventDetail {
|
|
|
234
186
|
readonly updatedAt?: string;
|
|
235
187
|
readonly [extra: string]: unknown;
|
|
236
188
|
}
|
|
237
|
-
/**
|
|
238
|
-
* Parsed, validated message payload exposed to consumers via events.
|
|
239
|
-
*/
|
|
189
|
+
/** A validated update as events expose it. */
|
|
240
190
|
interface PayloadLivePreviewData {
|
|
241
191
|
readonly fields: Record<string, unknown>;
|
|
242
192
|
readonly schema?: readonly PayloadFieldSchema[];
|
|
@@ -244,13 +194,7 @@ interface PayloadLivePreviewData {
|
|
|
244
194
|
readonly collectionSlug?: string;
|
|
245
195
|
readonly locale?: string;
|
|
246
196
|
}
|
|
247
|
-
/**
|
|
248
|
-
* The subset of Payload's field-schema description we rely on.
|
|
249
|
-
*
|
|
250
|
-
* Payload emits the entire field configuration tree but we only need
|
|
251
|
-
* the discriminator `type` and the nesting via `fields`/`blocks`.
|
|
252
|
-
* Extra properties are preserved opaquely.
|
|
253
|
-
*/
|
|
197
|
+
/** The subset of Payload's field schema the runtime reads; extra properties are kept opaquely. */
|
|
254
198
|
interface PayloadFieldSchema {
|
|
255
199
|
readonly name: string;
|
|
256
200
|
readonly type: PayloadFieldType;
|
|
@@ -271,54 +215,19 @@ interface PayloadBlockSchema {
|
|
|
271
215
|
readonly fields: readonly PayloadFieldSchema[];
|
|
272
216
|
readonly [extra: string]: unknown;
|
|
273
217
|
}
|
|
274
|
-
/**
|
|
275
|
-
* The Payload core field types this library recognises (20 as of
|
|
276
|
-
* Payload 3.x). `tabs` is treated as a structural container; `group`
|
|
277
|
-
* flattens nested fields.
|
|
278
|
-
*/
|
|
218
|
+
/** Payload's core field types. `tabs` is a structural container; `group` flattens. */
|
|
279
219
|
type PayloadFieldType = 'text' | 'textarea' | 'richText' | 'email' | 'number' | 'checkbox' | 'date' | 'select' | 'radio' | 'array' | 'blocks' | 'group' | 'tabs' | 'row' | 'collapsible' | 'relationship' | 'upload' | 'point' | 'json' | 'code' | 'ui';
|
|
280
|
-
/**
|
|
281
|
-
* Payload's `admin.condition` is a function in source, but it cannot
|
|
282
|
-
* be transferred over postMessage. The schema arrives with this slot
|
|
283
|
-
* either absent or, in future Payload versions, replaced by a
|
|
284
|
-
* serialized expression. We model the slot but do not consume it yet.
|
|
285
|
-
*/
|
|
220
|
+
/** `admin.condition` is a function in source and cannot cross `postMessage`; modelled, not consumed. */
|
|
286
221
|
type PayloadFieldCondition = (data: unknown, siblingData: unknown) => boolean;
|
|
287
222
|
|
|
288
223
|
/**
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
* rule, or a bug report referring to `LP0301` keeps meaning the same thing
|
|
293
|
-
* after the sentence around it is rewritten, and a code is greppable in a way
|
|
294
|
-
* that a sentence fragment is not.
|
|
295
|
-
*
|
|
296
|
-
* Codes are grouped by the question they answer:
|
|
297
|
-
*
|
|
298
|
-
* - `LP01xx` — configuration and origin trust: the runtime is running, but it
|
|
299
|
-
* was set up in a way that will bite.
|
|
300
|
-
* - `LP02xx` — bindings and markup: an update had nowhere to land.
|
|
301
|
-
* - `LP03xx` — updates and scheduling: the update landed somewhere other than
|
|
302
|
-
* the DOM, or not yet.
|
|
303
|
-
* - `LP04xx` — rendering: a value reached an element and was refused.
|
|
304
|
-
* - `LP05xx` — protocol and messages: something arrived that was not accepted.
|
|
305
|
-
* - `LP06xx` — consumer callbacks: code the consumer supplied threw.
|
|
306
|
-
* - `LP07xx` — audit findings from `pll doctor`, which reports on a served
|
|
307
|
-
* response rather than on a running runtime.
|
|
224
|
+
* Every diagnostic code the runtime and tools emit, so a consumer can branch
|
|
225
|
+
* on `e.code` instead of matching message text. A code is never reused for a
|
|
226
|
+
* different meaning; a retired one stays reserved.
|
|
308
227
|
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
* as a rejection and reported as `LP0502`, so there is nothing distinct to
|
|
313
|
-
* report yet. The number stays reserved rather than being handed to something
|
|
314
|
-
* else.
|
|
315
|
-
*
|
|
316
|
-
* The runtime writes these as string literals at each reporting site rather
|
|
317
|
-
* than reading them from this record, so the inline build pays for the six
|
|
318
|
-
* characters it prints and nothing more. This record exists so consumers can
|
|
319
|
-
* refer to a code by name instead of copying a literal.
|
|
320
|
-
*
|
|
321
|
-
* @module @core/diagnostic-codes
|
|
228
|
+
* Browser-side reporting sites write the literal (`code: 'LP0603'`) rather than
|
|
229
|
+
* reading it from this record: the frozen table would then ship in the inline
|
|
230
|
+
* runtime for the sake of six characters. Server-side tools may import it.
|
|
322
231
|
*/
|
|
323
232
|
/**
|
|
324
233
|
* Every diagnostic code the runtime can report, by name.
|
|
@@ -334,6 +243,8 @@ declare const DIAGNOSTIC_CODES: Readonly<{
|
|
|
334
243
|
readonly NoTrustedOrigin: "LP0101";
|
|
335
244
|
/** Origin trust rests on `document.referrer`, so any framing site is trusted. */
|
|
336
245
|
readonly ReferrerOnlyTrust: "LP0102";
|
|
246
|
+
/** A plugin declares a runtime range this runtime does not satisfy; it was refused. */
|
|
247
|
+
readonly PluginIncompatible: "LP0103";
|
|
337
248
|
/** An update named a field with no binding anchor on the page. */
|
|
338
249
|
readonly OrphanField: "LP0201";
|
|
339
250
|
/** Owner scoping is on and the update names no document it could belong to. */
|
|
@@ -346,6 +257,14 @@ declare const DIAGNOSTIC_CODES: Readonly<{
|
|
|
346
257
|
readonly TextTargetHasChildren: "LP0402";
|
|
347
258
|
/** A structural container has no array template, so the update was skipped. */
|
|
348
259
|
readonly MissingArrayTemplate: "LP0403";
|
|
260
|
+
/** A structural item has no `id`; it pairs positionally, so an insert re-renders every row after it. */
|
|
261
|
+
readonly StructuralItemUnkeyed: "LP0404";
|
|
262
|
+
/** Two structural items share a key; later ones pair positionally. */
|
|
263
|
+
readonly StructuralDuplicateKey: "LP0405";
|
|
264
|
+
/** Every structural key changed while the length did not; the source generates keys per message. */
|
|
265
|
+
readonly StructuralUnstableKeys: "LP0406";
|
|
266
|
+
/** A binding asks for a delivery strategy this release does not have; it is left unchanged. */
|
|
267
|
+
readonly UnsupportedStrategy: "LP0407";
|
|
349
268
|
/** A message was rejected before it reached the update pipeline. */
|
|
350
269
|
readonly MessageRejected: "LP0501";
|
|
351
270
|
/** A preview token was rejected. */
|
|
@@ -376,251 +295,245 @@ declare const DIAGNOSTIC_CODES: Readonly<{
|
|
|
376
295
|
readonly AuditNoBindings: "LP0707";
|
|
377
296
|
/** The URL did not return an HTML page, so nothing else can be judged. */
|
|
378
297
|
readonly AuditNotAPage: "LP0708";
|
|
298
|
+
/** A fragment request failed (network, timeout, server error); the boundary was patched instead. */
|
|
299
|
+
readonly FragmentRequestFailed: "LP0801";
|
|
300
|
+
/** The fragment response had the wrong content type or shape; the boundary was patched instead. */
|
|
301
|
+
readonly FragmentResponseInvalid: "LP0802";
|
|
302
|
+
/** The fragment endpoint refused the preview (not authorized); the boundary was patched instead. */
|
|
303
|
+
readonly FragmentUnauthorized: "LP0803";
|
|
304
|
+
/** A fragment response arrived for a revision that was already superseded and was discarded. */
|
|
305
|
+
readonly FragmentSuperseded: "LP0804";
|
|
306
|
+
/** A route refresh was requested again for the same revision; the loop guard stopped it. */
|
|
307
|
+
readonly RouteRefreshLoop: "LP0805";
|
|
308
|
+
/** A boundary asks for the fragment strategy but no fragment handler is configured; it is patched. */
|
|
309
|
+
readonly FragmentStrategyUnavailable: "LP0806";
|
|
310
|
+
/** A readiness row is not yet at its 2.0 value; `pll doctor --v2` reports it. */
|
|
311
|
+
readonly V2ReadinessGap: "LP0709";
|
|
312
|
+
/** The preview runtime is served to anonymous visitors, not only inside the admin frame. */
|
|
313
|
+
readonly RuntimeOnPublicPage: "LP0710";
|
|
379
314
|
}>;
|
|
380
315
|
/** A diagnostic code, as it appears in log output and on the `error` event. */
|
|
381
316
|
type DiagnosticCode = (typeof DIAGNOSTIC_CODES)[keyof typeof DIAGNOSTIC_CODES];
|
|
382
317
|
|
|
383
318
|
/**
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
*
|
|
389
|
-
* for free because the `EventEmitter` is generic over this map.
|
|
390
|
-
*
|
|
391
|
-
* @module @events/types
|
|
319
|
+
* How a bound region is brought up to date: the runtime patches it, a server
|
|
320
|
+
* renders a `data-payload-fragment` boundary from the unsaved form state, or
|
|
321
|
+
* the whole route refreshes. Core carries only the seam — which boundaries a
|
|
322
|
+
* strategy owns and what it may do — so a patch-only page pays for nothing
|
|
323
|
+
* more; the orchestration lives in `payload-live-preview/fragment`.
|
|
392
324
|
*/
|
|
393
325
|
|
|
326
|
+
/** What produced an update: the runtime's DOM patching, a server-rendered fragment, or a route refresh. */
|
|
327
|
+
type UpdateSource = 'patch' | 'fragment' | 'route';
|
|
328
|
+
/** One revision, as a strategy sees it, with the runtime capabilities it may use. */
|
|
329
|
+
interface FragmentContext {
|
|
330
|
+
readonly root: ParentNode;
|
|
331
|
+
readonly revision: number;
|
|
332
|
+
readonly receivedAt: number;
|
|
333
|
+
readonly fields: Readonly<Record<string, unknown>>;
|
|
334
|
+
readonly locale: string | undefined;
|
|
335
|
+
readonly collectionSlug: string | undefined;
|
|
336
|
+
readonly globalSlug: string | undefined;
|
|
337
|
+
/** Aborted when a newer revision arrives or the runtime stops. */
|
|
338
|
+
readonly signal: AbortSignal;
|
|
339
|
+
/** Whether this revision is still the current one. */
|
|
340
|
+
readonly isCurrent: () => boolean;
|
|
341
|
+
/** Morph server-rendered HTML into a boundary (Trusted Types and the keyed morph apply). */
|
|
342
|
+
readonly morph: (boundary: Element, html: string) => void;
|
|
343
|
+
/** Patch the boundary's own bindings from this revision — the deterministic fallback. */
|
|
344
|
+
readonly patch: (boundary: Element) => void;
|
|
345
|
+
/** Debug log through the runtime's logger, with the diagnostic code. */
|
|
346
|
+
readonly log: (code: DiagnosticCode, detail: string) => void;
|
|
347
|
+
/** Report a boundary rendered. */
|
|
348
|
+
readonly rendered: (boundary: Element, id: string, key: string | undefined) => void;
|
|
349
|
+
/** Report a boundary failed and patched instead; emits the `error` and `fragmentRender` events. */
|
|
350
|
+
readonly failed: (boundary: Element, id: string, key: string | undefined, code: DiagnosticCode, reason: string) => void;
|
|
351
|
+
}
|
|
352
|
+
interface FragmentReport {
|
|
353
|
+
readonly rendered: number;
|
|
354
|
+
readonly failed: number;
|
|
355
|
+
readonly superseded: number;
|
|
356
|
+
}
|
|
357
|
+
/** A fragment strategy: which boundaries it owns for a revision, and how it renders them. */
|
|
358
|
+
interface FragmentStrategy {
|
|
359
|
+
/** The boundaries this update re-renders; their inner bindings are left to the strategy. */
|
|
360
|
+
readonly plan: (root: ParentNode, changedFields: ReadonlySet<string>) => readonly Element[];
|
|
361
|
+
/** Render the planned boundaries; resolves once every one settled. */
|
|
362
|
+
readonly render: (context: FragmentContext, boundaries: readonly Element[]) => Promise<FragmentReport>;
|
|
363
|
+
}
|
|
364
|
+
/** One revision, as the route strategy sees it. */
|
|
365
|
+
interface RouteContext {
|
|
366
|
+
readonly revision: number;
|
|
367
|
+
readonly receivedAt: number;
|
|
368
|
+
/** Aborted when a newer revision arrives or the runtime stops. */
|
|
369
|
+
readonly signal: AbortSignal;
|
|
370
|
+
readonly isCurrent: () => boolean;
|
|
371
|
+
readonly log: (code: DiagnosticCode, detail: string) => void;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Whether a revision needs the whole route re-rendered, and how. After a
|
|
375
|
+
* refresh the runtime rescans and re-applies the revision, so the unsaved
|
|
376
|
+
* state lands on the fresh markup.
|
|
377
|
+
*/
|
|
378
|
+
interface RouteStrategy {
|
|
379
|
+
readonly plan: (root: ParentNode, changedFields: ReadonlySet<string>) => boolean;
|
|
380
|
+
readonly refresh: (context: RouteContext) => Promise<'refreshed' | 'failed' | 'superseded'>;
|
|
381
|
+
}
|
|
382
|
+
interface StrategyHandlers {
|
|
383
|
+
readonly fragment?: FragmentStrategy;
|
|
384
|
+
readonly route?: RouteStrategy;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** Public event payloads. The map keys are the valid event names; `EventEmitter` is generic over it. */
|
|
388
|
+
|
|
394
389
|
interface LivePreviewEventMap {
|
|
395
|
-
/**
|
|
396
|
-
* Fired once per startup attempt after observers, cache, and message listening
|
|
397
|
-
* are active. A later startup failure may roll that attempt back; retrying then
|
|
398
|
-
* emits a new `init` event.
|
|
399
|
-
*/
|
|
390
|
+
/** Once per startup attempt, after observers, cache and message listening are active. A rolled-back attempt emits it again on retry. */
|
|
400
391
|
readonly init: {
|
|
401
392
|
readonly timestamp: number;
|
|
402
393
|
};
|
|
403
|
-
/**
|
|
394
|
+
/** The first accepted data-bearing update from a trusted origin. */
|
|
404
395
|
readonly connect: {
|
|
405
396
|
readonly origin: string;
|
|
406
397
|
readonly timestamp: number;
|
|
407
398
|
};
|
|
408
|
-
/**
|
|
409
|
-
* Fired when the heartbeat times out or the runtime is destroyed. The
|
|
410
|
-
* `'unload'` reason remains in the public union for 1.x producer compatibility;
|
|
411
|
-
* the built-in runtime does not currently emit it.
|
|
412
|
-
*/
|
|
399
|
+
/** The heartbeat timed out, the runtime was destroyed, or the page was suspended for navigation (`'unload'`). */
|
|
413
400
|
readonly disconnect: {
|
|
414
401
|
readonly reason: 'timeout' | 'destroy' | 'unload';
|
|
415
402
|
readonly timestamp: number;
|
|
416
403
|
};
|
|
417
|
-
/**
|
|
418
|
-
* Fired before the DOM is mutated for an incoming update.
|
|
419
|
-
*
|
|
420
|
-
* Handlers may call `cancel()` before their synchronous or asynchronous
|
|
421
|
-
* work settles to skip exactly this revision.
|
|
422
|
-
*/
|
|
404
|
+
/** Before the DOM is mutated for an update; `cancel()` skips exactly this revision. */
|
|
423
405
|
readonly beforeUpdate: {
|
|
424
406
|
readonly data: PayloadLivePreviewData;
|
|
425
|
-
/** Runtime-populated ordering metadata; optional for 1.x producer compatibility. */
|
|
426
407
|
readonly revision?: number;
|
|
408
|
+
/** When the runtime accepted the message, Unix milliseconds. */
|
|
409
|
+
readonly receivedAt?: number;
|
|
410
|
+
/** The strategy that produced the update. */
|
|
411
|
+
readonly source?: UpdateSource;
|
|
427
412
|
readonly cancel: () => void;
|
|
428
413
|
};
|
|
429
|
-
/**
|
|
430
|
-
* Fired after each actual, current DOM application batch. Visibility replay
|
|
431
|
-
* may produce another event for the same revision; cancelled, obsolete, and
|
|
432
|
-
* deferred-only batches produce none.
|
|
433
|
-
*/
|
|
414
|
+
/** After each applied, still-current batch. Visibility replay may emit another for the same revision; cancelled, obsolete and deferred-only batches emit none. */
|
|
434
415
|
readonly afterUpdate: {
|
|
435
416
|
readonly data: PayloadLivePreviewData;
|
|
436
417
|
readonly updatedCount: number;
|
|
437
418
|
readonly durationMs: number;
|
|
438
|
-
/** Runtime-populated ordering metadata; optional for 1.x producer compatibility. */
|
|
439
419
|
readonly revision?: number;
|
|
420
|
+
readonly receivedAt?: number;
|
|
421
|
+
readonly source?: UpdateSource;
|
|
440
422
|
};
|
|
441
|
-
/**
|
|
423
|
+
/** Each successful, still-current element write. */
|
|
442
424
|
readonly elementUpdate: {
|
|
443
425
|
readonly element: Element;
|
|
444
426
|
readonly fieldName: string;
|
|
445
427
|
readonly previousValue: unknown;
|
|
446
428
|
readonly nextValue: unknown;
|
|
447
|
-
/** Runtime-populated ordering metadata; optional for 1.x producer compatibility. */
|
|
448
429
|
readonly revision?: number;
|
|
430
|
+
readonly receivedAt?: number;
|
|
431
|
+
readonly source?: UpdateSource;
|
|
449
432
|
};
|
|
450
|
-
/**
|
|
433
|
+
/** The element cache was rebuilt (initial scan or mutation). */
|
|
451
434
|
readonly cacheRefresh: {
|
|
452
435
|
readonly elementCount: number;
|
|
453
436
|
readonly fieldCount: number;
|
|
454
437
|
readonly durationMs: number;
|
|
455
438
|
};
|
|
456
|
-
/**
|
|
439
|
+
/** Once per fragment boundary and revision: rendered by the server, or failed (with the LP08xx code) and patched instead. */
|
|
440
|
+
readonly fragmentRender: {
|
|
441
|
+
readonly element: Element;
|
|
442
|
+
readonly id: string;
|
|
443
|
+
readonly key: string | undefined;
|
|
444
|
+
readonly status: 'rendered' | 'failed';
|
|
445
|
+
readonly code?: DiagnosticCode;
|
|
446
|
+
readonly revision: number;
|
|
447
|
+
readonly receivedAt: number;
|
|
448
|
+
};
|
|
449
|
+
/** A `payload-document-event` message arrived (document save). */
|
|
457
450
|
readonly documentSave: {
|
|
458
451
|
readonly timestamp: number;
|
|
459
452
|
};
|
|
460
|
-
/**
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
*/
|
|
453
|
+
/** A data update carried `externallyUpdatedRelationship`: a related document changed in an admin drawer, so the update re-renders unconditionally. */
|
|
454
|
+
readonly relationshipUpdate: {
|
|
455
|
+
readonly detail: PayloadDocumentEventDetail;
|
|
456
|
+
readonly timestamp: number;
|
|
457
|
+
};
|
|
458
|
+
/** An error the runtime caught but cannot fully recover from. Branch on `code`; `context` is the human-readable origin. */
|
|
467
459
|
readonly error: {
|
|
468
460
|
readonly error: Error;
|
|
469
461
|
readonly context: string;
|
|
470
462
|
readonly code: DiagnosticCode;
|
|
471
463
|
};
|
|
472
|
-
/**
|
|
473
|
-
* Fired during destroy after browser observers and the message listener are
|
|
474
|
-
* removed. Event/plugin listeners remain available for this notification and
|
|
475
|
-
* are released by the owning client afterwards.
|
|
476
|
-
*/
|
|
464
|
+
/** During destroy, after observers and the message listener are removed; listeners are released by the owning client afterwards. */
|
|
477
465
|
readonly destroy: {
|
|
478
466
|
readonly timestamp: number;
|
|
479
467
|
};
|
|
480
468
|
}
|
|
481
|
-
/**
|
|
482
|
-
* Handler signature: receives the payload and may return a Promise.
|
|
483
|
-
*
|
|
484
|
-
* Promise-returning handlers are awaited in registration order — this
|
|
485
|
-
* is important for `beforeUpdate.cancel()` semantics.
|
|
486
|
-
*/
|
|
469
|
+
/** Handlers may return a promise; they are awaited in registration order, which `beforeUpdate.cancel()` relies on. */
|
|
487
470
|
type EventHandler<TPayload> = (payload: TPayload) => void | Promise<void>;
|
|
488
|
-
/** Subscription removal function returned by `on()`/`once()`. */
|
|
489
471
|
type Unsubscribe = () => void;
|
|
490
472
|
|
|
491
473
|
/**
|
|
492
|
-
* Per-instance
|
|
493
|
-
*
|
|
494
|
-
* Replaces the legacy module-level singleton. Each `LivePreviewClient`
|
|
495
|
-
* instantiates its own emitter so that calling `destroy()` on one
|
|
496
|
-
* instance cannot clobber listeners attached to another.
|
|
497
|
-
*
|
|
498
|
-
* Design notes:
|
|
499
|
-
*
|
|
500
|
-
* - Generic over an event map type. The default `LivePreviewEventMap`
|
|
501
|
-
* captures every event this library emits, but the implementation
|
|
502
|
-
* is reusable for any map.
|
|
503
|
-
* - Handlers may be `async`. They are awaited sequentially in
|
|
504
|
-
* registration order. This is intentional: `beforeUpdate.cancel()`
|
|
505
|
-
* and similar synchronization rely on stable ordering.
|
|
506
|
-
* - Errors thrown by one handler are logged via `console.error` and
|
|
507
|
-
* do not prevent later handlers from running.
|
|
508
|
-
* - `once()` semantics: registered into a separate map; the entire
|
|
509
|
-
* snapshot for an event is claimed immediately before its dispatch,
|
|
510
|
-
* and remains consumed even if individual handlers throw.
|
|
511
|
-
*
|
|
512
|
-
* @module @events/emitter
|
|
474
|
+
* Per-instance typed emitter: handlers run sequentially in registration order,
|
|
475
|
+
* may be async, and a throwing one is logged and isolated. See ADR 0002.
|
|
513
476
|
*/
|
|
514
477
|
|
|
515
478
|
declare class EventEmitter<TMap extends object = LivePreviewEventMap> {
|
|
516
479
|
#private;
|
|
517
|
-
/**
|
|
518
|
-
* Subscribe `handler` to `event`. Returns an unsubscribe function;
|
|
519
|
-
* call it to remove the handler. The handler is invoked for every
|
|
520
|
-
* subsequent emit until unsubscribed.
|
|
521
|
-
*/
|
|
522
480
|
on<E extends keyof TMap>(event: E, handler: EventHandler<TMap[E]>): Unsubscribe;
|
|
523
|
-
/**
|
|
524
|
-
* Subscribe `handler` for a single emit. After the first dispatch,
|
|
525
|
-
* the handler is removed automatically.
|
|
526
|
-
*/
|
|
527
481
|
once<E extends keyof TMap>(event: E, handler: EventHandler<TMap[E]>): Unsubscribe;
|
|
528
|
-
/**
|
|
529
|
-
* Remove `handler` from `event`. Works for both `on`- and
|
|
530
|
-
* `once`-registered handlers. No-op when the handler isn't registered.
|
|
531
|
-
*/
|
|
532
482
|
off<E extends keyof TMap>(event: E, handler: EventHandler<TMap[E]>): void;
|
|
533
|
-
/**
|
|
534
|
-
* Dispatch `payload` to every handler registered for `event`.
|
|
535
|
-
*
|
|
536
|
-
* Handlers run sequentially in registration order — regular ones
|
|
537
|
-
* first, then `once` handlers. Async handlers are awaited. Synchronous
|
|
538
|
-
* exceptions are caught, logged, and isolated to the offending handler.
|
|
539
|
-
*/
|
|
483
|
+
/** Dispatch to every handler: regular ones first, then `once` handlers. */
|
|
540
484
|
emit<E extends keyof TMap>(event: E, payload: TMap[E]): Promise<void>;
|
|
541
485
|
/**
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
* later callbacks. `emit()` intentionally keeps its established behaviour.
|
|
545
|
-
*
|
|
546
|
-
* Once-handlers are claimed only when dispatch reaches the once phase. Once
|
|
547
|
-
* claimed, the whole snapshot remains consumed even if the predicate changes
|
|
548
|
-
* while one of its handlers is awaiting, matching normal once semantics.
|
|
549
|
-
*
|
|
486
|
+
* Dispatch while `shouldContinue()` holds — checked around every handler, so
|
|
487
|
+
* an obsolete transaction cannot resume across an await.
|
|
550
488
|
* @internal
|
|
551
|
-
* @returns `true` when the complete handler sequence ran while eligible.
|
|
552
489
|
*/
|
|
553
490
|
emitWhile<E extends keyof TMap>(event: E, payload: TMap[E], shouldContinue: () => boolean): Promise<boolean>;
|
|
554
|
-
/**
|
|
555
|
-
* Number of handlers currently registered for `event`.
|
|
556
|
-
* Sum of `on` and `once` registrations.
|
|
557
|
-
*/
|
|
491
|
+
/** Registered handlers for `event`, `on` and `once` together. */
|
|
558
492
|
listenerCount(event: keyof TMap): number;
|
|
559
|
-
/**
|
|
560
|
-
* Remove every handler. Pass an event name to limit the removal to
|
|
561
|
-
* one event; omit it to clear every event on this emitter.
|
|
562
|
-
*
|
|
563
|
-
* Crucially this only affects the current instance — there is no
|
|
564
|
-
* shared global state.
|
|
565
|
-
*/
|
|
493
|
+
/** Remove every handler, or only those of `event`. */
|
|
566
494
|
removeAllListeners(event?: keyof TMap): void;
|
|
567
|
-
/**
|
|
568
|
-
* Snapshot of all event names that currently have at least one
|
|
569
|
-
* registered handler. Useful for debugging.
|
|
570
|
-
*/
|
|
495
|
+
/** Event names with at least one handler. */
|
|
571
496
|
eventNames(): (keyof TMap)[];
|
|
572
497
|
}
|
|
573
498
|
|
|
574
|
-
/**
|
|
575
|
-
* Core type definitions shared across all runtime primitives.
|
|
576
|
-
*
|
|
577
|
-
* @module @core/types
|
|
578
|
-
*/
|
|
499
|
+
/** Types shared by the runtime primitives. */
|
|
579
500
|
|
|
580
|
-
/**
|
|
581
|
-
* Field types recognized by the live preview renderer.
|
|
582
|
-
*
|
|
583
|
-
* Extends Payload's field set with `html` and `url` which are not
|
|
584
|
-
* Payload field types per se but useful DOM-binding categories.
|
|
585
|
-
*/
|
|
501
|
+
/** Payload's field set plus the DOM-binding categories `html`, `url`, `image` and `structural-array`. */
|
|
586
502
|
type FieldType = PayloadFieldType | 'html' | 'url' | 'image' | 'structural-array';
|
|
503
|
+
/** A namespaced project renderer key, so a typo of a built-in type can never become one. */
|
|
504
|
+
type CustomRendererKey = `${string}:${string}`;
|
|
505
|
+
/** What a renderer may be registered under and an element may ask for. */
|
|
506
|
+
type RendererKey = FieldType | CustomRendererKey;
|
|
587
507
|
/**
|
|
588
|
-
* A
|
|
589
|
-
*
|
|
590
|
-
* Stored in the element cache indexed by `fieldName`. Multiple cached
|
|
591
|
-
* entries can share a field name when the same field is rendered in
|
|
592
|
-
* several places (e.g., a title shown in both the header and the
|
|
593
|
-
* page body).
|
|
508
|
+
* A project rich-text renderer, shared by SSR and preview. Its output goes
|
|
509
|
+
* through the sanitizer; call the same `sanitizeHtml()` server-side for byte equality.
|
|
594
510
|
*/
|
|
511
|
+
type RichTextRenderer = (value: unknown, context: {
|
|
512
|
+
readonly fieldName: string;
|
|
513
|
+
readonly element: Element;
|
|
514
|
+
readonly locale: string | undefined;
|
|
515
|
+
}) => string;
|
|
516
|
+
/** One registered binding. Several may share a field name when it renders in several places. */
|
|
595
517
|
interface CachedElement {
|
|
596
518
|
/** The bound DOM element. */
|
|
597
519
|
readonly element: Element;
|
|
598
520
|
/** Payload field path, e.g., `title` or `hero.subtitle`. */
|
|
599
521
|
readonly fieldName: string;
|
|
600
|
-
/**
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
* The cache resolves this from DOM attributes (`data-payload-type`,
|
|
604
|
-
* tag-based heuristics) at build time. When `explicitFieldType` is
|
|
605
|
-
* `false` the lifecycle is allowed to override this with the type
|
|
606
|
-
* learned from the schema once `fieldSchemaJSON` arrives.
|
|
607
|
-
*/
|
|
608
|
-
readonly fieldType: FieldType;
|
|
609
|
-
/**
|
|
610
|
-
* `true` when the field type was set by an explicit
|
|
611
|
-
* `data-payload-type` attribute (consumer trumps schema), `false`
|
|
612
|
-
* when it was inferred via tag-based heuristics (schema may
|
|
613
|
-
* override later). Optional so synthetic bindings (tests / programmatic
|
|
614
|
-
* `cache.add()` callers) need not supply it; the cache always
|
|
615
|
-
* populates it for DOM-derived bindings.
|
|
616
|
-
*/
|
|
522
|
+
/** Resolved from attributes and tag heuristics; the schema may override it unless explicit. */
|
|
523
|
+
readonly fieldType: RendererKey;
|
|
524
|
+
/** Whether `data-payload-type` set the type, in which case the schema must not override it. */
|
|
617
525
|
readonly explicitFieldType?: boolean;
|
|
618
|
-
/**
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
526
|
+
/** Fields this binding depends on (`data-payload-depends`); a change in any re-applies it under `skipUnchanged`. */
|
|
527
|
+
readonly dependsOn?: readonly string[];
|
|
528
|
+
/** Raw `data-payload-strategy` value. */
|
|
529
|
+
readonly strategy?: string;
|
|
530
|
+
/** How the runtime brings this binding up to date; `'unknown'` for an unrecognised strategy. Resolved by the cache. */
|
|
531
|
+
readonly strategyKind?: UpdateSource | 'unknown';
|
|
532
|
+
/** Nearest enclosing `data-payload-fragment` boundary. Resolved by the cache. */
|
|
533
|
+
readonly fragmentBoundary?: Element;
|
|
534
|
+
/** `data-payload-boundary`: an anchor that hides itself while its field is empty. */
|
|
535
|
+
readonly boundary?: boolean;
|
|
536
|
+
/** Write the value into this attribute instead of the content; policed writes. */
|
|
624
537
|
readonly targetAttribute?: string;
|
|
625
538
|
/** Optional sibling-field path bound to the element's `href` attribute. */
|
|
626
539
|
readonly hrefField?: string;
|
|
@@ -634,21 +547,10 @@ interface CachedElement {
|
|
|
634
547
|
readonly arraySeparator?: string;
|
|
635
548
|
/** Optional locale code locked onto this element (overrides the global locale). */
|
|
636
549
|
readonly locale?: string;
|
|
637
|
-
/**
|
|
638
|
-
* Optional document this binding belongs to, resolved from the nearest
|
|
639
|
-
* `data-payload-owner` ancestor (the element itself included).
|
|
640
|
-
*
|
|
641
|
-
* Only consulted while owner scoping is enabled. Without it a field name is
|
|
642
|
-
* the sole identity, so every document rendered on the page competes for the
|
|
643
|
-
* same name.
|
|
644
|
-
*/
|
|
550
|
+
/** The document this binding belongs to, from the nearest `data-payload-owner`. */
|
|
645
551
|
readonly owner?: string;
|
|
646
552
|
}
|
|
647
|
-
/**
|
|
648
|
-
* Context passed to every field renderer. Provides access to the
|
|
649
|
-
* entire field tree so renderers can resolve sibling fields (e.g.,
|
|
650
|
-
* an `<a>` that pulls `href` from a different field).
|
|
651
|
-
*/
|
|
553
|
+
/** What a renderer is given; `allFields` lets it resolve a sibling field. */
|
|
652
554
|
interface RenderContext {
|
|
653
555
|
/** Full field map for the current update — used for sibling lookups. */
|
|
654
556
|
readonly allFields: Record<string, unknown>;
|
|
@@ -656,315 +558,221 @@ interface RenderContext {
|
|
|
656
558
|
readonly locale: string | undefined;
|
|
657
559
|
/** Optional schema descriptor for the field, when available. */
|
|
658
560
|
readonly schema: PayloadFieldSchema | undefined;
|
|
561
|
+
/** The project's rich-text renderer, when the client was configured with one. */
|
|
562
|
+
readonly renderRichText?: RichTextRenderer;
|
|
659
563
|
}
|
|
660
564
|
/**
|
|
661
|
-
*
|
|
662
|
-
*
|
|
663
|
-
*
|
|
664
|
-
* binding plus the new value and apply it to the element. They must
|
|
665
|
-
* not throw — failures should be silenced and logged so that one bad
|
|
666
|
-
* field cannot stop an entire update. The public renderer result remains
|
|
667
|
-
* `void` for 1.x compatibility. Built-in renderers may use an internal exact
|
|
668
|
-
* `false` sentinel when they deliberately perform no DOM write; callers must
|
|
669
|
-
* not rely on that implementation detail.
|
|
565
|
+
* A renderer writes one value into its element. It must not throw: one bad
|
|
566
|
+
* field may not stop an update. The result stays `void` — the internal
|
|
567
|
+
* no-write sentinel is not part of the contract.
|
|
670
568
|
*/
|
|
671
569
|
interface FieldRenderer {
|
|
672
|
-
readonly name:
|
|
570
|
+
readonly name: RendererKey;
|
|
673
571
|
render(target: CachedElement, value: unknown, context: RenderContext): void;
|
|
674
572
|
}
|
|
675
573
|
|
|
676
574
|
/**
|
|
677
|
-
* Plugin
|
|
678
|
-
*
|
|
679
|
-
*
|
|
680
|
-
* scoped, per-instance context. Plugins can register custom field
|
|
681
|
-
* renderers, transform incoming values, and subscribe to events —
|
|
682
|
-
* the same primitives the built-in renderers use.
|
|
683
|
-
*
|
|
684
|
-
* The system is intentionally minimal. Anything that can be expressed
|
|
685
|
-
* with an event subscription belongs in `events`, not `plugins`.
|
|
686
|
-
*
|
|
687
|
-
* @module @plugins/types
|
|
575
|
+
* Plugin compatibility claims. The range grammar is the subset a plugin
|
|
576
|
+
* author writes (`*`, `1.2.3`, `^`, `~`, `>=`, `>`, `<`, `<=`, space-joined
|
|
577
|
+
* conjunctions, `||` alternatives); prerelease tags are ignored.
|
|
688
578
|
*/
|
|
579
|
+
interface PluginCompatibility {
|
|
580
|
+
/** A semver range the runtime's `VERSION` must satisfy, e.g. `>=2.0.0`. */
|
|
581
|
+
readonly runtime?: string;
|
|
582
|
+
/** The highest postMessage protocol version the plugin understands. */
|
|
583
|
+
readonly protocol?: number;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/** Plugin contract: a scoped, per-instance context for renderers, transforms and events. */
|
|
689
587
|
|
|
690
588
|
/** Idempotent handle that releases one plugin-owned resource. */
|
|
691
589
|
type PluginDisposer = () => void;
|
|
692
|
-
/**
|
|
693
|
-
|
|
694
|
-
*
|
|
695
|
-
* This remains nominally compatible with the pre-1.0.4 `EventEmitter` context
|
|
696
|
-
* type. Its methods are registration-scoped: even bulk removal affects only
|
|
697
|
-
* this plugin's subscriptions, never a consumer's or another plugin's
|
|
698
|
-
* listeners.
|
|
699
|
-
*/
|
|
700
|
-
interface PluginEvents extends EventEmitter {
|
|
590
|
+
/** The client's event surface scoped to one registration: bulk removal and introspection see only this plugin's listeners. */
|
|
591
|
+
interface PluginEvents {
|
|
701
592
|
readonly on: <E extends keyof LivePreviewEventMap>(event: E, handler: EventHandler<LivePreviewEventMap[E]>) => Unsubscribe;
|
|
702
593
|
readonly once: <E extends keyof LivePreviewEventMap>(event: E, handler: EventHandler<LivePreviewEventMap[E]>) => Unsubscribe;
|
|
703
594
|
readonly off: <E extends keyof LivePreviewEventMap>(event: E, handler: EventHandler<LivePreviewEventMap[E]>) => void;
|
|
704
595
|
/** Emit through the owning client's event channel. */
|
|
705
596
|
readonly emit: <E extends keyof LivePreviewEventMap>(event: E, payload: LivePreviewEventMap[E]) => Promise<void>;
|
|
706
|
-
/**
|
|
707
|
-
* Guarded emit through the owning client channel. Dispatch stops when the
|
|
708
|
-
* caller predicate becomes false or this plugin registration is removed.
|
|
709
|
-
*/
|
|
597
|
+
/** Guarded emit; dispatch stops when the predicate turns false or this registration is removed. */
|
|
710
598
|
readonly emitWhile: <E extends keyof LivePreviewEventMap>(event: E, payload: LivePreviewEventMap[E], shouldContinue: () => boolean) => Promise<boolean>;
|
|
711
|
-
/** Number of listeners owned by this plugin registration. */
|
|
712
599
|
readonly listenerCount: (event: keyof LivePreviewEventMap) => number;
|
|
713
|
-
/** Remove only listeners owned by this plugin registration. */
|
|
714
600
|
readonly removeAllListeners: (event?: keyof LivePreviewEventMap) => void;
|
|
715
|
-
/** Event names for which this plugin registration owns listeners. */
|
|
716
601
|
readonly eventNames: () => (keyof LivePreviewEventMap)[];
|
|
717
602
|
}
|
|
718
603
|
/**
|
|
719
|
-
* Synchronous
|
|
720
|
-
*
|
|
721
|
-
* order and each receives the preceding transform's result. The final value is
|
|
722
|
-
* frozen into that entry before later renderer or attribute dispatch;
|
|
723
|
-
* `allFields` remains the merged, untransformed update snapshot.
|
|
724
|
-
*
|
|
725
|
-
* Returning a different shape than the input is allowed, but returning a
|
|
726
|
-
* Promise or other thenable is a contract error. If a transform throws or
|
|
727
|
-
* returns a thenable, the chain stops, the runtime emits an `error` event, and
|
|
728
|
-
* dispatch falls back to the original merged value. That fallback still passes
|
|
729
|
-
* through the normal renderer and attribute security controls.
|
|
604
|
+
* Synchronous value transform run in registration order; returning a thenable
|
|
605
|
+
* is a contract error (`LP0602`) that dispatches the untransformed value.
|
|
730
606
|
*/
|
|
731
607
|
type FieldTransform = (value: unknown, context: {
|
|
732
608
|
readonly fieldName: string;
|
|
733
609
|
readonly element: Element;
|
|
734
610
|
readonly allFields: Record<string, unknown>;
|
|
735
611
|
}) => unknown;
|
|
736
|
-
/**
|
|
737
|
-
* Per-instance plugin context. Replaces the legacy singleton context.
|
|
738
|
-
*
|
|
739
|
-
* Plugins receive a context that is bound to the parent
|
|
740
|
-
* `LivePreviewClient`. They can:
|
|
741
|
-
*
|
|
742
|
-
* - subscribe via `events.on/once`,
|
|
743
|
-
* - register field renderers,
|
|
744
|
-
* - register value transforms,
|
|
745
|
-
* - read the client's (frozen) configuration,
|
|
746
|
-
* - log through the client's debug channel.
|
|
747
|
-
*/
|
|
748
612
|
interface PluginContext {
|
|
749
613
|
readonly events: PluginEvents;
|
|
750
|
-
/** Register a renderer owned by this
|
|
614
|
+
/** Register a renderer layer owned by this registration. */
|
|
751
615
|
readonly registerFieldRenderer: (renderer: FieldRenderer) => void;
|
|
752
|
-
/** Register an ordered transform owned by this
|
|
616
|
+
/** Register an ordered transform owned by this registration. */
|
|
753
617
|
readonly registerTransform: (fieldName: string, transform: FieldTransform) => void;
|
|
754
|
-
/**
|
|
755
|
-
* Register any additional synchronous cleanup owned by this registration.
|
|
756
|
-
* The manager invokes it during rollback, `unuse()`, or client destruction.
|
|
757
|
-
* Optional so pre-1.0.4 structural context mocks remain assignable.
|
|
758
|
-
*/
|
|
618
|
+
/** Register a synchronous cleanup run on rollback, `unuse()` and client destruction. */
|
|
759
619
|
readonly registerCleanup?: (cleanup: PluginDisposer) => void;
|
|
760
620
|
readonly getConfig: () => Readonly<Record<string, unknown>>;
|
|
761
621
|
readonly log: (...args: unknown[]) => void;
|
|
762
622
|
}
|
|
763
|
-
/**
|
|
764
|
-
* Plugin definition. `init` runs once per registration; `destroy`
|
|
765
|
-
* runs when the plugin is unregistered or the client is destroyed.
|
|
766
|
-
*
|
|
767
|
-
* Both hooks may return a promise; the manager awaits them.
|
|
768
|
-
*/
|
|
623
|
+
/** `init` runs once per registration, `destroy` on removal; both may return a promise. */
|
|
769
624
|
interface LivePreviewPlugin {
|
|
770
625
|
readonly name: string;
|
|
771
626
|
readonly version?: string;
|
|
627
|
+
/** Checked at registration; a plugin this runtime does not fit is refused (`LP0103`). */
|
|
628
|
+
readonly compat?: PluginCompatibility;
|
|
772
629
|
readonly init: (context: PluginContext) => void | Promise<void>;
|
|
773
630
|
readonly destroy?: () => void | Promise<void>;
|
|
774
631
|
}
|
|
775
632
|
|
|
776
633
|
/**
|
|
777
|
-
* `
|
|
778
|
-
*
|
|
779
|
-
*
|
|
634
|
+
* The two defaults profiles, row by row. 2.0 ships `v2`; `defaults: 'v1'` opts
|
|
635
|
+
* back into the 1.x table for a staged migration. Each row belongs to exactly
|
|
636
|
+
* one side — the adapter's request decision or the browser runtime — and a
|
|
637
|
+
* test asserts the profile assigns every readiness row that has an option, so
|
|
638
|
+
* a row added later cannot be forgotten. See ADR 0007.
|
|
639
|
+
*/
|
|
640
|
+
/** `'v2'` is the 2.0 default; `'v1'` is the 1.x table. */
|
|
641
|
+
type DefaultsProfile = 'v1' | 'v2';
|
|
642
|
+
/** How the runtime decides which windows may post updates. */
|
|
643
|
+
type EventSourcePolicy = 'any' | 'parent-or-opener';
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* `LivePreviewClient` configuration. Defaults are the 2.0 rows; `defaults: 'v1'`
|
|
647
|
+
* restores the 1.x values named per option (ADR 0007).
|
|
780
648
|
*/
|
|
649
|
+
|
|
781
650
|
interface LivePreviewClientConfig {
|
|
782
|
-
/** Explicit
|
|
651
|
+
/** Explicit trusted admin origins. */
|
|
783
652
|
readonly allowedOrigins?: readonly string[];
|
|
784
|
-
/**
|
|
785
|
-
* Payload server origin, e.g. `https://cms.example.com`. When set,
|
|
786
|
-
* every incoming update is re-fetched through the Payload REST API
|
|
787
|
-
* so relationship/upload fields render populated instead of as bare
|
|
788
|
-
* IDs (same strategy as the official client). Recommended for
|
|
789
|
-
* Payload 3.x.
|
|
790
|
-
*/
|
|
653
|
+
/** Payload origin; every update is then re-fetched through the REST API so relations arrive populated. Requires `mergeDepth`. */
|
|
791
654
|
readonly serverURL?: string;
|
|
792
|
-
/** REST
|
|
655
|
+
/** REST route prefix used with `serverURL`. Default `/api`. */
|
|
793
656
|
readonly apiRoute?: string;
|
|
794
|
-
/** Population depth used with `serverURL`.
|
|
657
|
+
/** Population depth used with `serverURL`. Required with it; `defaults: 'v1'` falls back to `1`. */
|
|
795
658
|
readonly mergeDepth?: number;
|
|
796
|
-
/**
|
|
797
|
-
* Custom fetch implementation for the `serverURL` merge request —
|
|
798
|
-
* the equivalent of the official client's `requestHandler`. Use it
|
|
799
|
-
* to attach auth headers or route through your own proxy. Only
|
|
800
|
-
* available on the programmatic client (functions cannot be
|
|
801
|
-
* serialised into the inline script).
|
|
802
|
-
*/
|
|
659
|
+
/** Custom fetch for the `serverURL` merge request: attach auth headers or route through a proxy. */
|
|
803
660
|
readonly mergeFetch?: typeof fetch;
|
|
804
|
-
/**
|
|
661
|
+
/** Verbose console output. Defaults to dev-mode detection. */
|
|
805
662
|
readonly debug?: boolean;
|
|
806
|
-
/** Debounce window for incoming updates.
|
|
663
|
+
/** Debounce window for incoming updates. Default 50 ms. */
|
|
807
664
|
readonly debounceMs?: number;
|
|
808
|
-
/**
|
|
809
|
-
* Heartbeat timeout in ms. Defaults to `0` (disabled) because the
|
|
810
|
-
* Payload admin sends messages only on form edits — there is no
|
|
811
|
-
* protocol keepalive, so an idle timeout would produce false
|
|
812
|
-
* disconnects.
|
|
813
|
-
*/
|
|
665
|
+
/** Heartbeat timeout in ms; `0` disables it (default), because the admin posts only on edits. */
|
|
814
666
|
readonly heartbeatMs?: number;
|
|
815
|
-
/** IntersectionObserver rootMargin
|
|
667
|
+
/** IntersectionObserver `rootMargin`. Default `200px`. */
|
|
816
668
|
readonly intersectionRootMargin?: string;
|
|
817
|
-
/**
|
|
818
|
-
* Restrict each update to the bindings owned by the document it describes.
|
|
819
|
-
*
|
|
820
|
-
* Ownership is declared in markup with `data-payload-owner`, resolved from
|
|
821
|
-
* the nearest ancestor: `global:<slug>`, `collection:<slug>`, or
|
|
822
|
-
* `collection:<slug>:<id>`. Payload already sends the edited document's
|
|
823
|
-
* identity, so nothing else needs configuring.
|
|
824
|
-
*
|
|
825
|
-
* Defaults to `false`, which keeps the 1.x behaviour of matching on the
|
|
826
|
-
* field name alone. Enable it when one page previews more than one document
|
|
827
|
-
* and a field name is therefore no longer a unique identity. While enabled,
|
|
828
|
-
* a binding without an owner is never updated.
|
|
829
|
-
*/
|
|
669
|
+
/** Update only bindings under a matching `data-payload-owner`; an unowned binding is then never updated. Default `false`. */
|
|
830
670
|
readonly scopeBindingsByOwner?: boolean;
|
|
831
|
-
/**
|
|
671
|
+
/** Skip a binding whose value did not change. Default `true`; `defaults: 'v1'` sets `false`. */
|
|
672
|
+
readonly skipUnchanged?: boolean;
|
|
673
|
+
/** Scroll the preview to the field being edited when its value changes. Default `false`. */
|
|
674
|
+
readonly revealEditedField?: boolean;
|
|
675
|
+
/** Fields whose change re-applies other bindings regardless of their own value: `{ price: ['priceLabel'] }`. Used with `skipUnchanged`. */
|
|
676
|
+
readonly dependencies?: Readonly<Record<string, readonly string[]>>;
|
|
677
|
+
/** Strategy handlers beyond patching; `createFragmentStrategy()` from `payload-live-preview/fragment` builds one. */
|
|
678
|
+
readonly strategies?: StrategyHandlers;
|
|
679
|
+
/** Apply every update regardless of visibility. Default `false`. */
|
|
832
680
|
readonly disableVisibilityGate?: boolean;
|
|
833
|
-
/** Cache
|
|
681
|
+
/** Cache size above which off-screen updates wait for intersection. Default `50`. */
|
|
834
682
|
readonly visibilityGateThreshold?: number;
|
|
835
|
-
/**
|
|
836
|
-
* Mount an `aria-live` region for connections, applied updates, and mounted
|
|
837
|
-
* heartbeat-timeout disconnects. Destroy releases it synchronously. Default `true`.
|
|
838
|
-
*/
|
|
683
|
+
/** Mount an `aria-live` region for connections, applied updates and heartbeat disconnects. Default `true`. */
|
|
839
684
|
readonly enableA11y?: boolean;
|
|
840
|
-
/** Locale
|
|
685
|
+
/** Locale of the announcement strings. Defaults to the detected locale. */
|
|
841
686
|
readonly a11yLocale?: string;
|
|
842
|
-
/** Document root
|
|
687
|
+
/** Document root. Default `document`. */
|
|
843
688
|
readonly root?: Document | Element;
|
|
844
|
-
/**
|
|
689
|
+
/** Renderer resolution ahead of the registry; return `undefined` to fall through. */
|
|
690
|
+
readonly resolveRenderer?: (fieldType: RendererKey, target: CachedElement) => FieldRenderer | undefined;
|
|
691
|
+
/** The project's rich-text renderer, shared with SSR. Its output is sanitized. */
|
|
692
|
+
readonly renderRichText?: RichTextRenderer;
|
|
693
|
+
/** Ignore `document.referrer` as an origin source. Default `true`; `defaults: 'v1'` sets `false`. */
|
|
845
694
|
readonly disableReferrerDetection?: boolean;
|
|
846
|
-
/** Disable the
|
|
695
|
+
/** Disable the dev-mode localhost matcher. Default `false`. */
|
|
847
696
|
readonly disableLocalhostMatching?: boolean;
|
|
848
|
-
/**
|
|
697
|
+
/** Which windows may post updates. Default `'parent-or-opener'`; `defaults: 'v1'` sets `'any'`. */
|
|
698
|
+
readonly eventSourcePolicy?: EventSourcePolicy;
|
|
699
|
+
/** Sanitizer policy for rich text and HTML writes. Default `'strict'`; `defaults: 'v1'` sets `'compat'`. */
|
|
700
|
+
readonly sanitizerPolicy?: 'compat' | 'strict';
|
|
701
|
+
/** `'v2'` (default) is the 2.0 table; `'v1'` restores every 1.x row at once. Explicit options win. */
|
|
702
|
+
readonly defaults?: DefaultsProfile;
|
|
703
|
+
/** Start the runtime in the constructor. Default `true`. */
|
|
849
704
|
readonly autoStart?: boolean;
|
|
850
|
-
/**
|
|
851
|
-
* Optional preview-token validator. When set, every data update
|
|
852
|
-
* must carry a `previewToken` field that this function approves.
|
|
853
|
-
*
|
|
854
|
-
* ⚠️ `previewToken` is an extension of THIS library, not part of the
|
|
855
|
-
* stock Payload postMessage protocol — the Payload admin never sends
|
|
856
|
-
* one. Only enable this when a custom admin component adds the token
|
|
857
|
-
* to outgoing messages; against an unmodified Payload admin it would
|
|
858
|
-
* drop every update.
|
|
859
|
-
*/
|
|
705
|
+
/** Preview-token validator; every update must then carry a `previewToken` it approves. Stock Payload sends none. */
|
|
860
706
|
readonly validateToken?: (token: string | undefined, origin: string) => boolean | Promise<boolean>;
|
|
861
707
|
}
|
|
862
708
|
|
|
863
709
|
/**
|
|
864
|
-
*
|
|
865
|
-
*
|
|
866
|
-
*
|
|
867
|
-
*
|
|
868
|
-
* - Per-instance event emitter (no singleton).
|
|
869
|
-
* - Per-instance plugin manager.
|
|
870
|
-
* - Custom-renderer registration.
|
|
871
|
-
* - Field-name transforms frozen into revision-bound scheduler entries before
|
|
872
|
-
* their later attribute or renderer dispatch.
|
|
873
|
-
*
|
|
874
|
-
* Consumers who want full programmatic control instantiate
|
|
875
|
-
* `LivePreviewClient` directly. Consumers who just want a working
|
|
876
|
-
* inline preview should use `generateInlineScript()` instead.
|
|
877
|
-
*
|
|
878
|
-
* @module @client
|
|
710
|
+
* `LivePreviewClient`: a `LivePreviewRuntime` with its own emitter, plugin
|
|
711
|
+
* manager and renderer registry. A page that only needs a working preview uses
|
|
712
|
+
* `generateInlineScript()` instead.
|
|
879
713
|
*/
|
|
880
714
|
|
|
881
715
|
declare class LivePreviewClient {
|
|
882
716
|
#private;
|
|
883
|
-
constructor(
|
|
717
|
+
constructor(rawConfig?: LivePreviewClientConfig);
|
|
884
718
|
/**
|
|
885
|
-
* Start the runtime
|
|
886
|
-
*
|
|
887
|
-
* active client also return `true`; `false` means the client was destroyed or
|
|
888
|
-
* the page is outside a preview context. A deferred startup can still fail,
|
|
889
|
-
* roll back, and be retried by a later call.
|
|
719
|
+
* Start the runtime: `true` while eligible and started or scheduled for DOM
|
|
720
|
+
* readiness, `false` once destroyed or outside a preview context.
|
|
890
721
|
*/
|
|
891
722
|
start(): boolean;
|
|
892
723
|
/**
|
|
893
|
-
* Release the message ingress
|
|
894
|
-
*
|
|
895
|
-
*
|
|
896
|
-
* does not re-run module scripts, so a client that stays attached across
|
|
897
|
-
* `pagehide` returns bound to a frozen page and silently stops updating.
|
|
898
|
-
* Plugins, renderers and transforms survive; `resume()` brings the same
|
|
899
|
-
* client back. Returns `false` when there was nothing running to suspend.
|
|
724
|
+
* Release the message ingress, keeping plugins and renderers, for `pagehide`:
|
|
725
|
+
* a bfcache restore re-runs no script, so an attached client would return
|
|
726
|
+
* bound to a frozen page. `false` when nothing was running.
|
|
900
727
|
*/
|
|
901
728
|
suspend(): boolean;
|
|
902
|
-
/**
|
|
903
|
-
* Reacquire after `suspend()`.
|
|
904
|
-
*
|
|
905
|
-
* Deliberately the same path as `start()`: a restored document needs the
|
|
906
|
-
* cache rebuilt, the observers rebound and the handshake rebroadcast, which
|
|
907
|
-
* is exactly what starting does. Returns `false` for a destroyed client or
|
|
908
|
-
* one that was never started.
|
|
909
|
-
*/
|
|
729
|
+
/** Reacquire after `suspend()`: the same path as `start()`, because a restored document needs the same rebuild. */
|
|
910
730
|
resume(): boolean;
|
|
911
|
-
/**
|
|
912
|
-
* Stop the runtime and tear down every plugin. Idempotent.
|
|
913
|
-
*/
|
|
731
|
+
/** Stop the runtime and tear down every plugin. Idempotent; concurrent callers share one promise. */
|
|
914
732
|
destroy(): Promise<void>;
|
|
915
|
-
/**
|
|
916
|
-
* Register a plugin.
|
|
917
|
-
*/
|
|
918
733
|
use(plugin: LivePreviewPlugin): Promise<void>;
|
|
919
|
-
/** Unregister a plugin and release
|
|
734
|
+
/** Unregister a plugin and release everything its registration owned. Still allowed from a destroy hook during teardown. */
|
|
920
735
|
unuse(name: string): Promise<void>;
|
|
921
|
-
/** Rebuild the element cache manually. */
|
|
922
736
|
refreshCache(): void;
|
|
923
|
-
/** Read-only access to the event emitter for `on`/`once`/`off`. */
|
|
924
737
|
get events(): EventEmitter;
|
|
925
|
-
/** Names of
|
|
738
|
+
/** Names of the registered plugins. */
|
|
926
739
|
get plugins(): readonly string[];
|
|
927
|
-
/** Current connection status. */
|
|
928
740
|
get status(): 'disconnected' | 'connecting' | 'connected';
|
|
929
|
-
/** Number of valid updates received so far. */
|
|
930
741
|
/**
|
|
931
|
-
* Point-in-time read of runtime state,
|
|
932
|
-
*
|
|
933
|
-
*
|
|
934
|
-
* Read `bindings.orphanFields` when a field refuses to update, and
|
|
935
|
-
* `scheduler.deferred` together with `scheduler.visibilityGateActive` when
|
|
936
|
-
* updates stop below the fold.
|
|
742
|
+
* Point-in-time read of runtime state, no I/O: `bindings.orphanFields` when a
|
|
743
|
+
* field refuses to update, `scheduler.deferred` when updates stop below the fold.
|
|
937
744
|
*/
|
|
938
745
|
inspect(): LivePreviewInspection;
|
|
746
|
+
/** Number of accepted updates so far. */
|
|
939
747
|
get updateCount(): number;
|
|
940
748
|
/** `true` once `destroy()` has been called. */
|
|
941
749
|
get destroyed(): boolean;
|
|
942
750
|
}
|
|
751
|
+
/** A started client, or `null` when the page is not a preview context. */
|
|
752
|
+
declare function initLivePreview(config?: LivePreviewClientConfig): LivePreviewClient | null;
|
|
753
|
+
|
|
943
754
|
/**
|
|
944
|
-
*
|
|
945
|
-
*
|
|
946
|
-
* Useful for SSR-style integrations that import this from any context.
|
|
755
|
+
* Admin-side reporter for "reveal the edited section": it names the field the
|
|
756
|
+
* editor's cursor is in so the preview scrolls there without a keystroke.
|
|
947
757
|
*/
|
|
948
|
-
|
|
758
|
+
/** The preview window (usually `iframe.contentWindow`) messages are posted to. */
|
|
759
|
+
interface FocusReportTarget {
|
|
760
|
+
postMessage: (message: unknown, targetOrigin: string) => void;
|
|
761
|
+
}
|
|
762
|
+
interface PreviewFocusMessage {
|
|
763
|
+
readonly type: 'payload-live-preview-focus';
|
|
764
|
+
readonly field: string;
|
|
765
|
+
}
|
|
766
|
+
/** Post one focus report. `targetOrigin` must be the preview's exact origin, never `'*'`. */
|
|
767
|
+
declare function reportPreviewFocus(target: FocusReportTarget, field: string, targetOrigin: string): void;
|
|
768
|
+
/** A reporter bound to a preview window and origin; `resolveTarget` runs per report, so a re-created iframe is addressed freshly. */
|
|
769
|
+
declare function createPreviewFocusReporter(resolveTarget: () => FocusReportTarget | null | undefined, targetOrigin: string): (field: string) => void;
|
|
949
770
|
|
|
950
771
|
/**
|
|
951
|
-
*
|
|
952
|
-
*
|
|
953
|
-
*
|
|
954
|
-
*
|
|
955
|
-
*
|
|
956
|
-
* - A **back/forward-cache restore** does not re-run module scripts. A client
|
|
957
|
-
* that stays attached across `pagehide` comes back bound to a document the
|
|
958
|
-
* browser froze and thawed, and simply stops updating.
|
|
959
|
-
* - A **soft navigation** replaces page content without a new document. The
|
|
960
|
-
* bindings the cache holds are gone, and nothing tells the runtime.
|
|
961
|
-
*
|
|
962
|
-
* This binds both to `suspend()`/`resume()`/`refreshCache()` in one place, so a
|
|
963
|
-
* consumer that uses no framework adapter can own the lifecycle with one call
|
|
964
|
-
* instead of re-deriving it — which is what every integration has had to do so
|
|
965
|
-
* far.
|
|
966
|
-
*
|
|
967
|
-
* @module @core/navigation-lifecycle
|
|
772
|
+
* Binds the document lifecycle the runtime cannot see: a back/forward-cache
|
|
773
|
+
* restore re-runs no scripts, so a client that stayed attached comes back
|
|
774
|
+
* bound to a frozen document and quietly stops updating; a soft navigation
|
|
775
|
+
* replaces the content the binding cache describes. Both are silent failures.
|
|
968
776
|
*/
|
|
969
777
|
/** The part of the client this owner drives. */
|
|
970
778
|
interface NavigationLifecycleTarget {
|
|
@@ -973,30 +781,17 @@ interface NavigationLifecycleTarget {
|
|
|
973
781
|
refreshCache(): void;
|
|
974
782
|
}
|
|
975
783
|
interface NavigationLifecycleOptions {
|
|
976
|
-
/**
|
|
977
|
-
* Event target for `pagehide`/`pageshow`. Defaults to `window`.
|
|
978
|
-
*
|
|
979
|
-
* These fire on the window, not the document, and only there — binding them
|
|
980
|
-
* anywhere else silently never fires.
|
|
981
|
-
*/
|
|
784
|
+
/** Where `pagehide`/`pageshow` fire; they exist only on the window. Defaults to `window`. */
|
|
982
785
|
readonly windowTarget?: EventTarget;
|
|
983
|
-
/**
|
|
984
|
-
* Event target for soft-navigation events. Defaults to `document`.
|
|
985
|
-
*/
|
|
786
|
+
/** Event target for soft-navigation events. Defaults to `document`. */
|
|
986
787
|
readonly documentTarget?: EventTarget;
|
|
987
788
|
/**
|
|
988
|
-
*
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
* Empty by default: the package cannot know which framework is present, and
|
|
992
|
-
* guessing would either miss the event or rebuild on an unrelated one.
|
|
789
|
+
* Events after which the DOM was replaced and the cache must be rebuilt, e.g.
|
|
790
|
+
* `astro:page-load`. Empty by default: only the host knows which its router fires.
|
|
993
791
|
*/
|
|
994
792
|
readonly softNavigationEvents?: readonly string[];
|
|
995
793
|
}
|
|
996
|
-
/**
|
|
997
|
-
* Bind the document lifecycle. Returns an unbind function; calling it twice is
|
|
998
|
-
* harmless.
|
|
999
|
-
*/
|
|
794
|
+
/** Returns an unbind function; calling it twice is harmless. */
|
|
1000
795
|
declare function bindNavigationLifecycle(target: NavigationLifecycleTarget, options?: NavigationLifecycleOptions): () => void;
|
|
1001
796
|
|
|
1002
797
|
interface RuntimeBuildInfo {
|
|
@@ -1005,536 +800,417 @@ interface RuntimeBuildInfo {
|
|
|
1005
800
|
}
|
|
1006
801
|
|
|
1007
802
|
/**
|
|
1008
|
-
*
|
|
1009
|
-
*
|
|
1010
|
-
*
|
|
1011
|
-
* configuration. Returns a JavaScript string suitable for embedding
|
|
1012
|
-
* via `<script>` in any framework.
|
|
1013
|
-
*
|
|
1014
|
-
* The runtime body is identical to the one used by the high-level
|
|
1015
|
-
* client because both originate from `src/core/runtime.ts`. There is
|
|
1016
|
-
* no parallel implementation to drift out of sync.
|
|
1017
|
-
*
|
|
1018
|
-
* @module @inline/generator
|
|
803
|
+
* The inline script's configuration and its positional wire order: the
|
|
804
|
+
* generator writes the tuple, `src/core/runtime.ts` destructures it, and
|
|
805
|
+
* `INLINE_CONFIG_KEYS` is where they agree. Append only, never reorder.
|
|
1019
806
|
*/
|
|
1020
807
|
|
|
1021
808
|
interface InlineScriptConfig {
|
|
1022
|
-
/**
|
|
809
|
+
/** Trusted admin origins, merged with the detected ones. */
|
|
1023
810
|
readonly allowedOrigins?: readonly string[];
|
|
1024
|
-
/**
|
|
1025
|
-
* Payload server origin, e.g. `https://cms.example.com`. When set,
|
|
1026
|
-
* every incoming update is re-fetched through the Payload REST API
|
|
1027
|
-
* (`X-Payload-HTTP-Method-Override: GET`, same strategy as the
|
|
1028
|
-
* official client) so relationship and upload fields render
|
|
1029
|
-
* populated instead of as bare IDs. Strongly recommended for
|
|
1030
|
-
* Payload 3.x. Requires the preview page to be able to reach the
|
|
1031
|
-
* API with credentials (same-site cookies or CORS `credentials`).
|
|
1032
|
-
*/
|
|
811
|
+
/** Payload origin; every update is then re-fetched through the REST API with credentials. Requires `mergeDepth`. */
|
|
1033
812
|
readonly serverURL?: string;
|
|
1034
|
-
/** REST
|
|
813
|
+
/** REST route prefix used with `serverURL`. Default `/api`. */
|
|
1035
814
|
readonly apiRoute?: string;
|
|
1036
|
-
/** Population depth used with `serverURL`.
|
|
815
|
+
/** Population depth used with `serverURL`. Required with `serverURL` unless `defaults: 'v1'`. */
|
|
1037
816
|
readonly mergeDepth?: number;
|
|
1038
|
-
/**
|
|
817
|
+
/** Verbose console logging. Default `false`. */
|
|
1039
818
|
readonly debug?: boolean;
|
|
1040
|
-
/** Debounce window for incoming updates in ms.
|
|
819
|
+
/** Debounce window for incoming updates in ms. Default `50`. */
|
|
1041
820
|
readonly debounceMs?: number;
|
|
1042
|
-
/**
|
|
821
|
+
/** Mount the screen-reader live region. Default `true`. */
|
|
1043
822
|
readonly enableA11y?: boolean;
|
|
1044
|
-
/**
|
|
1045
|
-
* Heartbeat timeout in ms. Defaults to `0` (disabled). The Payload
|
|
1046
|
-
* admin only posts messages on form edits — there is no protocol
|
|
1047
|
-
* keepalive — so an idle-based timeout would fire spurious
|
|
1048
|
-
* disconnects while the editor pauses. Enable only if your admin
|
|
1049
|
-
* setup sends periodic messages.
|
|
1050
|
-
*/
|
|
823
|
+
/** Heartbeat timeout in ms; `0` disables it (default), because the admin posts only on edits. */
|
|
1051
824
|
readonly heartbeatMs?: number;
|
|
1052
|
-
/**
|
|
1053
|
-
* Bypass the visibility-gate optimisation and apply every update
|
|
1054
|
-
* regardless of viewport position. Default `false` — the gate kicks
|
|
1055
|
-
* in when the cache exceeds `visibilityGateThreshold` entries.
|
|
1056
|
-
*/
|
|
825
|
+
/** Apply every update regardless of viewport position. Default `false`. */
|
|
1057
826
|
readonly disableVisibilityGate?: boolean;
|
|
1058
|
-
/**
|
|
1059
|
-
* Cache-size threshold above which the visibility gate activates.
|
|
1060
|
-
* Default `50`. Off-screen elements above this threshold are queued
|
|
1061
|
-
* for replay on intersection instead of being updated immediately.
|
|
1062
|
-
*/
|
|
827
|
+
/** Cache size above which off-screen updates wait for intersection. Default `50`. */
|
|
1063
828
|
readonly visibilityGateThreshold?: number;
|
|
1064
|
-
/**
|
|
1065
|
-
* `rootMargin` passed to the `IntersectionObserver` watching the
|
|
1066
|
-
* cached elements. Default `'200px'`. Increase to pre-render
|
|
1067
|
-
* further off-screen content; decrease for tighter visibility.
|
|
1068
|
-
*/
|
|
829
|
+
/** `rootMargin` of the IntersectionObserver. Default `'200px'`. */
|
|
1069
830
|
readonly intersectionRootMargin?: string;
|
|
1070
|
-
/**
|
|
1071
|
-
* Opt out of `document.referrer`-based origin auto-detection.
|
|
1072
|
-
* Useful when the page is loaded under `Referrer-Policy: no-referrer`
|
|
1073
|
-
* and you want predictable behaviour. Default `false`.
|
|
1074
|
-
*/
|
|
831
|
+
/** Ignore `document.referrer` as an origin source. Default `true`; `defaults: 'v1'` sets `false`. */
|
|
1075
832
|
readonly disableReferrerDetection?: boolean;
|
|
1076
|
-
/**
|
|
1077
|
-
* Opt out of the dev-mode localhost-pattern matcher (any port on
|
|
1078
|
-
* `localhost` / `127.0.0.1`). Useful when running locally against
|
|
1079
|
-
* a production-like origin list. Default `false`.
|
|
1080
|
-
*/
|
|
833
|
+
/** Disable the dev-mode localhost matcher (any port on `localhost`/`127.0.0.1`). Default `false`. */
|
|
1081
834
|
readonly disableLocalhostMatching?: boolean;
|
|
1082
|
-
/**
|
|
1083
|
-
* Restrict each update to the bindings owned by the document it describes,
|
|
1084
|
-
* declared in markup with `data-payload-owner` and resolved from the nearest
|
|
1085
|
-
* ancestor. Default `false`, which keeps matching on the field name alone.
|
|
1086
|
-
*
|
|
1087
|
-
* Enable it when one page previews more than one document. While enabled a
|
|
1088
|
-
* binding without an owner is never updated.
|
|
1089
|
-
*/
|
|
835
|
+
/** Update only bindings under a matching `data-payload-owner`. Default `false`. */
|
|
1090
836
|
readonly scopeBindingsByOwner?: boolean;
|
|
1091
|
-
/**
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
readonly
|
|
837
|
+
/** Skip bindings whose value did not change. Default `true`; `defaults: 'v1'` sets `false`. */
|
|
838
|
+
readonly skipUnchanged?: boolean;
|
|
839
|
+
/** Scroll the preview to the field being edited. Default `false`. */
|
|
840
|
+
readonly revealEditedField?: boolean;
|
|
841
|
+
/** Which windows may post updates. Default `'parent-or-opener'`; `defaults: 'v1'` sets `'any'`. */
|
|
842
|
+
readonly eventSourcePolicy?: 'any' | 'parent-or-opener';
|
|
843
|
+
/** Sanitizer policy for rich text and HTML writes. Default `'strict'`; `defaults: 'v1'` sets `'compat'`. */
|
|
844
|
+
readonly sanitizerPolicy?: 'compat' | 'strict';
|
|
845
|
+
/** Same-origin path of a fragment endpoint; the script then carries the fragment client ahead of the runtime (ADR 0011). */
|
|
846
|
+
readonly fragmentEndpoint?: string;
|
|
847
|
+
/** Which defaults the omitted options fall back to. Not serialized: `'v1'` only relaxes the generator's `mergeDepth` check. */
|
|
848
|
+
readonly defaults?: DefaultsProfile;
|
|
1099
849
|
}
|
|
850
|
+
|
|
1100
851
|
/**
|
|
1101
|
-
*
|
|
1102
|
-
* with
|
|
1103
|
-
*
|
|
1104
|
-
* The string does NOT include the `<script>` tags — use
|
|
1105
|
-
* `wrapWithScriptTag()` when you need them.
|
|
852
|
+
* Wraps the build-time runtime IIFE with the consumer's configuration; a page
|
|
853
|
+
* with a fragment endpoint gets the fragment prelude ahead of it.
|
|
1106
854
|
*/
|
|
855
|
+
|
|
856
|
+
/** The inline script body without `<script>` tags; see `wrapWithScriptTag()`. */
|
|
1107
857
|
declare function generateInlineScript(config?: InlineScriptConfig): string;
|
|
1108
|
-
/**
|
|
1109
|
-
* Wrap an inline script body in a `<script>` tag. When a nonce is
|
|
1110
|
-
* provided it is added as the `nonce` attribute so CSP policies that
|
|
1111
|
-
* require `'nonce-...'` accept the script.
|
|
1112
|
-
*/
|
|
858
|
+
/** Wrap a script body in `<script>`, with the CSP nonce attribute when given. */
|
|
1113
859
|
declare function wrapWithScriptTag(body: string, options?: {
|
|
1114
860
|
nonce?: string;
|
|
1115
861
|
}): string;
|
|
1116
|
-
/**
|
|
1117
|
-
* Snapshot of the build-time information for diagnostics.
|
|
1118
|
-
*/
|
|
1119
862
|
declare function runtimeBuildInfo(): RuntimeBuildInfo;
|
|
1120
863
|
|
|
1121
|
-
/**
|
|
1122
|
-
*
|
|
1123
|
-
*
|
|
1124
|
-
* Implementations follow the OWASP XSS Prevention Cheat Sheet: each
|
|
1125
|
-
* special character is replaced with its decimal or hex entity. The
|
|
1126
|
-
* functions are isomorphic — they have no dependency on the DOM and
|
|
1127
|
-
* can run in any JavaScript environment (browser, Node, Deno, Bun).
|
|
1128
|
-
*
|
|
1129
|
-
* @module @security/escape
|
|
1130
|
-
*/
|
|
1131
|
-
/**
|
|
1132
|
-
* Escape every HTML-significant character in `text` so the result is
|
|
1133
|
-
* safe to interpolate into any HTML context (element body or attribute).
|
|
1134
|
-
*
|
|
1135
|
-
* - Coerces non-strings via `String(value)`; pass `null`/`undefined`
|
|
1136
|
-
* intentionally — they become `"null"`/`"undefined"`.
|
|
1137
|
-
* - Cannot fail; returns `""` for the empty input.
|
|
1138
|
-
*/
|
|
864
|
+
/** HTML and CSS escaping primitives (OWASP XSS Prevention Cheat Sheet). DOM-free. */
|
|
865
|
+
/** Escape `text` for an element body or a *quoted* attribute value; strings only, and unquoted attributes are not covered. */
|
|
1139
866
|
declare function escapeHtml(text: string): string;
|
|
1140
|
-
/**
|
|
1141
|
-
* Escape characters that would break out of a double-quoted HTML
|
|
1142
|
-
* attribute value. Stricter than `escapeHtml` for body content but
|
|
1143
|
-
* preserves URL characters (`/`, `=`, etc.) that are common in
|
|
1144
|
-
* `href` and `src` values.
|
|
1145
|
-
*
|
|
1146
|
-
* Only call this for values that have already been validated as safe
|
|
1147
|
-
* URLs or trusted strings; it does NOT block dangerous protocols.
|
|
1148
|
-
*/
|
|
867
|
+
/** Escape a value for a *quoted* HTML attribute, leaving URL characters intact; it validates no scheme, so run `isSafeUrl()` on `href`/`src` first. */
|
|
1149
868
|
declare function escapeHtmlAttribute(value: string): string;
|
|
1150
869
|
|
|
1151
870
|
/**
|
|
1152
|
-
* URL safety
|
|
1153
|
-
*
|
|
1154
|
-
* Single source of truth for URL-protocol decisions used by both the
|
|
1155
|
-
* inline runtime and the high-level client. The previous version of
|
|
1156
|
-
* this library had two implementations that subtly disagreed; this
|
|
1157
|
-
* module replaces both.
|
|
1158
|
-
*
|
|
1159
|
-
* Policy:
|
|
1160
|
-
* - Allow only the protocols in `SAFE_PROTOCOLS`.
|
|
1161
|
-
* - Reject every other protocol explicitly, including `javascript:`,
|
|
1162
|
-
* `data:`, `vbscript:`, `file:`, `blob:`, and any custom scheme.
|
|
1163
|
-
* - Empty strings are NOT considered safe (this is a tightening from
|
|
1164
|
-
* the legacy behaviour and avoids producing `<a href="">` markup).
|
|
1165
|
-
*
|
|
1166
|
-
* @module @security/url-validator
|
|
1167
|
-
*/
|
|
1168
|
-
/**
|
|
1169
|
-
* Returns `true` only when `url` is one of the explicitly allowed forms:
|
|
1170
|
-
*
|
|
1171
|
-
* - Absolute URL with `http:`, `https:`, `mailto:`, or `tel:` protocol
|
|
1172
|
-
* - Protocol-relative URL (`//example.com/...`)
|
|
1173
|
-
* - Same-origin absolute path (`/foo`)
|
|
1174
|
-
* - Hash or query fragment (`#section`, `?q=1`)
|
|
1175
|
-
* - Plain relative path (`foo/bar`, `./foo`, `../foo`)
|
|
871
|
+
* URL safety: the one scheme allow-list the runtime, the client and the
|
|
872
|
+
* sanitizer share. Empty strings are not safe (`<a href="">` is never emitted).
|
|
1176
873
|
*/
|
|
874
|
+
/** `true` only for absolute `http`/`https`/`mailto`/`tel` URLs, protocol-relative URLs, paths, `#`/`?` fragments and relative paths. */
|
|
1177
875
|
declare function isSafeUrl(url: unknown): boolean;
|
|
1178
|
-
/**
|
|
1179
|
-
* Returns `true` when the URL points to an external HTTP(S) origin.
|
|
1180
|
-
* Protocol-relative URLs (`//example.com/...`) count as external —
|
|
1181
|
-
* they resolve to another origin and need the same `noopener`
|
|
1182
|
-
* hardening as absolute ones.
|
|
1183
|
-
*
|
|
1184
|
-
* Used by renderers to decide whether to add `target="_blank"` and the
|
|
1185
|
-
* `noopener noreferrer` rel attributes to anchors. Inputs that fail
|
|
1186
|
-
* `isSafeUrl()` always return `false`.
|
|
1187
|
-
*/
|
|
876
|
+
/** Whether a safe URL points at another HTTP(S) origin, protocol-relative forms included, and so needs `noopener` hardening. */
|
|
1188
877
|
declare function isExternalHttpUrl(url: string): boolean;
|
|
1189
878
|
|
|
879
|
+
/**
|
|
880
|
+
* `'strict'` (default) strips `id`, `name` and `data-payload-*` and passes
|
|
881
|
+
* other `data-*` only via `allowedDataAttributes`; `'compat'` keeps them.
|
|
882
|
+
*/
|
|
883
|
+
type SanitizerPolicyMode = 'compat' | 'strict';
|
|
1190
884
|
interface SanitizeOptions {
|
|
1191
|
-
/**
|
|
885
|
+
/** Overrides the module default set by `setSanitizerPolicy()`. */
|
|
886
|
+
readonly policy?: SanitizerPolicyMode;
|
|
887
|
+
/** `data-*` attributes (full names) that pass under `'strict'`; `data-payload-*` never does. */
|
|
888
|
+
readonly allowedDataAttributes?: readonly string[];
|
|
889
|
+
/**
|
|
890
|
+
* Keep `input`, `button`, `textarea`, `select`, `option` and `label` — only
|
|
891
|
+
* for markup the page author wrote, never for CMS content. `form`,
|
|
892
|
+
* handlers, `style` and unsafe URLs stay dropped.
|
|
893
|
+
*/
|
|
894
|
+
readonly allowFormControls?: boolean;
|
|
895
|
+
/** Extra tags to allow. Lower-case, untrimmed. */
|
|
1192
896
|
readonly additionalAllowedTags?: readonly string[];
|
|
1193
|
-
/**
|
|
897
|
+
/**
|
|
898
|
+
* Extra per-tag attributes, lower-case. Cannot re-admit handlers, `style`,
|
|
899
|
+
* or — under `'strict'` — `id`, `name` and `data-payload-*`.
|
|
900
|
+
*/
|
|
1194
901
|
readonly additionalAllowedAttributes?: Readonly<Record<string, readonly string[]>>;
|
|
902
|
+
/**
|
|
903
|
+
* The input is a page-author item template, so `'strict'` keeps the
|
|
904
|
+
* applier's reconciliation attributes (`TEMPLATE_ATTRIBUTES`) and nothing
|
|
905
|
+
* else — every other `data-payload-*` is still stripped, so a template can
|
|
906
|
+
* never add a binding.
|
|
907
|
+
*/
|
|
908
|
+
readonly templateMode?: boolean;
|
|
1195
909
|
}
|
|
1196
|
-
/**
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
* thin adapter) that produces `<template>`-style elements works.
|
|
1200
|
-
*/
|
|
910
|
+
/** Set the policy for calls without an explicit `policy`. The runtime sets it from `sanitizerPolicy`. */
|
|
911
|
+
declare function setSanitizerPolicy(mode: SanitizerPolicyMode): void;
|
|
912
|
+
/** The `Document` surface the sanitizer needs; jsdom, linkedom and happy-dom all provide it. */
|
|
1201
913
|
interface SanitizerDocument {
|
|
1202
914
|
createElement: (tagName: string) => {
|
|
1203
915
|
innerHTML: string;
|
|
1204
916
|
readonly content: ParentNode;
|
|
1205
917
|
};
|
|
1206
918
|
}
|
|
919
|
+
/** Supply a `Document` for SSR, e.g. linkedom's `parseHTML(...).document`; `null` clears it. */
|
|
920
|
+
declare function setSanitizerDocument(doc: SanitizerDocument | null): void;
|
|
1207
921
|
/**
|
|
1208
|
-
*
|
|
1209
|
-
* `globalThis.document` is unavailable.
|
|
1210
|
-
*
|
|
1211
|
-
* Server-side renderers (Node, Bun, Deno without DOM globals) can wire
|
|
1212
|
-
* this up once at startup with any of the popular pure-JS DOM
|
|
1213
|
-
* libraries:
|
|
1214
|
-
*
|
|
1215
|
-
* ```ts
|
|
1216
|
-
* // linkedom — recommended (smallest, fastest)
|
|
1217
|
-
* import { parseHTML } from 'linkedom';
|
|
1218
|
-
* import { setSanitizerDocument } from 'payload-live-preview';
|
|
1219
|
-
* const { document } = parseHTML('<!doctype html><html><body></body></html>');
|
|
1220
|
-
* setSanitizerDocument(document);
|
|
1221
|
-
* ```
|
|
1222
|
-
*
|
|
1223
|
-
* ```ts
|
|
1224
|
-
* // jsdom — heavier, but already in many SSR pipelines
|
|
1225
|
-
* import { JSDOM } from 'jsdom';
|
|
1226
|
-
* setSanitizerDocument(new JSDOM().window.document);
|
|
1227
|
-
* ```
|
|
1228
|
-
*
|
|
1229
|
-
* Pass `null` to clear a previous override (mostly useful for tests).
|
|
1230
|
-
*/
|
|
1231
|
-
declare function setSanitizerDocument(doc: SanitizerDocument | null): void;
|
|
1232
|
-
/**
|
|
1233
|
-
* Sanitize `html` and return safe HTML.
|
|
1234
|
-
*
|
|
1235
|
-
* The function never returns `undefined`/`null`. On malformed input the
|
|
1236
|
-
* browser parser silently recovers and the sanitizer continues from
|
|
1237
|
-
* the recovered tree.
|
|
922
|
+
* Sanitize `html`; malformed input is recovered by the parser first.
|
|
1238
923
|
*
|
|
1239
924
|
* @throws {SanitizerEnvironmentError} when no DOM is available.
|
|
1240
925
|
*/
|
|
1241
926
|
declare function sanitizeHtml(html: string, options?: SanitizeOptions): string;
|
|
1242
927
|
|
|
1243
928
|
/**
|
|
1244
|
-
* Content-Security-Policy helpers
|
|
1245
|
-
*
|
|
1246
|
-
* Two responsibilities:
|
|
1247
|
-
*
|
|
1248
|
-
* 1. Generate cryptographic nonces so consumers can opt into a strict
|
|
1249
|
-
* `script-src 'nonce-...'` policy and avoid `'unsafe-inline'`.
|
|
1250
|
-
* 2. Build the `frame-ancestors` directive value, restricting which
|
|
1251
|
-
* origins may embed the preview page in an iframe.
|
|
1252
|
-
*
|
|
1253
|
-
* Both functions are isomorphic — they work in browsers, Node, and
|
|
1254
|
-
* edge runtimes that expose Web Crypto via `globalThis.crypto`.
|
|
1255
|
-
*
|
|
1256
|
-
* @module @security/csp
|
|
1257
|
-
*/
|
|
1258
|
-
/**
|
|
1259
|
-
* Minimal Web-Crypto surface area we actually call. The full `Crypto`
|
|
1260
|
-
* type drags in browser-only declarations that don't typecheck cleanly
|
|
1261
|
-
* against Node's `webcrypto` shape — this narrower interface accepts
|
|
1262
|
-
* both without an unsafe cast.
|
|
929
|
+
* Content-Security-Policy helpers: nonce generation, directive builders and
|
|
930
|
+
* the header merge every adapter uses. Isomorphic; needs only Web Crypto.
|
|
1263
931
|
*/
|
|
932
|
+
/** The one Web Crypto method this module calls; accepts Node's `webcrypto` without a cast. */
|
|
1264
933
|
interface WebCryptoLike {
|
|
1265
934
|
getRandomValues: <T extends ArrayBufferView | null>(array: T) => T;
|
|
1266
935
|
}
|
|
1267
|
-
/**
|
|
1268
|
-
* Inject a Web Crypto implementation to use when `globalThis.crypto`
|
|
1269
|
-
* is unavailable.
|
|
1270
|
-
*
|
|
1271
|
-
* Node.js exposed `globalThis.crypto` globally starting with v19. On
|
|
1272
|
-
* Node 18 (still LTS at time of writing) consumers can flip the
|
|
1273
|
-
* `--experimental-global-webcrypto` flag *or* call this function once
|
|
1274
|
-
* at startup:
|
|
1275
|
-
*
|
|
1276
|
-
* ```ts
|
|
1277
|
-
* import { webcrypto } from 'node:crypto';
|
|
1278
|
-
* import { setCspCrypto } from 'payload-live-preview';
|
|
1279
|
-
* setCspCrypto(webcrypto as { getRandomValues: typeof webcrypto.getRandomValues });
|
|
1280
|
-
* ```
|
|
1281
|
-
*
|
|
1282
|
-
* Browser bundles never need to call this — `globalThis.crypto` is
|
|
1283
|
-
* always present there, and the override only matters when the global
|
|
1284
|
-
* is missing.
|
|
1285
|
-
*
|
|
1286
|
-
* Pass `null` to clear a previous override (mostly useful for tests).
|
|
1287
|
-
*/
|
|
936
|
+
/** Supply a Web Crypto implementation for runtimes without `globalThis.crypto`; `null` clears it. */
|
|
1288
937
|
declare function setCspCrypto(crypto: WebCryptoLike | null): void;
|
|
1289
|
-
/**
|
|
1290
|
-
* Generate a base64url-encoded cryptographic nonce.
|
|
1291
|
-
*
|
|
1292
|
-
* Uses `crypto.getRandomValues` everywhere — falls back to throwing
|
|
1293
|
-
* if no Web Crypto implementation is available, because a predictable
|
|
1294
|
-
* nonce would silently defeat the entire CSP.
|
|
1295
|
-
*
|
|
1296
|
-
* @param bytes Number of random bytes (defaults to 16 → 128-bit entropy).
|
|
1297
|
-
*/
|
|
938
|
+
/** A base64url nonce of `bytes` random bytes (default 16); throws without Web Crypto, since a predictable nonce silently defeats the CSP. */
|
|
1298
939
|
declare function generateCspNonce(bytes?: number): string;
|
|
1299
940
|
interface FrameAncestorsOptions {
|
|
1300
941
|
readonly self?: boolean;
|
|
1301
942
|
readonly origins?: readonly string[];
|
|
1302
943
|
readonly allowNone?: boolean;
|
|
1303
944
|
}
|
|
1304
|
-
/**
|
|
1305
|
-
* Build the value for `frame-ancestors` in a `Content-Security-Policy`
|
|
1306
|
-
* header.
|
|
1307
|
-
*
|
|
1308
|
-
* Returns `'none'` if `allowNone: true` and no other sources are
|
|
1309
|
-
* provided. Otherwise concatenates `'self'` (if requested) with the
|
|
1310
|
-
* given origins, deduplicating and trimming entries.
|
|
1311
|
-
*
|
|
1312
|
-
* @example
|
|
1313
|
-
* `frame-ancestors ${buildFrameAncestors({ self: true, origins: [adminUrl] })}`
|
|
1314
|
-
*/
|
|
945
|
+
/** The `frame-ancestors` value: `'self'` and the origins, deduplicated; `'none'` when empty. */
|
|
1315
946
|
declare function buildFrameAncestors(options?: FrameAncestorsOptions): string;
|
|
1316
947
|
/**
|
|
1317
|
-
*
|
|
1318
|
-
*
|
|
1319
|
-
*
|
|
1320
|
-
* `'strict-dynamic'` is **opt-in**: under CSP 3 it makes browsers
|
|
1321
|
-
* ignore `'self'` and every host source in the directive, so any
|
|
1322
|
-
* parser-inserted script without a nonce (framework hydration scripts,
|
|
1323
|
-
* consumer `<script>` tags) would be blocked. Only enable it when every
|
|
1324
|
-
* script on the page carries the nonce.
|
|
948
|
+
* A `script-src` value permitting one nonce plus optional extra sources.
|
|
949
|
+
* `'strict-dynamic'` is opt-in: it makes CSP 3 browsers ignore `'self'` and
|
|
950
|
+
* host sources, so every script on the page must then carry the nonce.
|
|
1325
951
|
*/
|
|
1326
952
|
declare function buildScriptSrcWithNonce(nonce: string, options?: {
|
|
1327
953
|
readonly self?: boolean;
|
|
1328
954
|
readonly extra?: readonly string[];
|
|
1329
955
|
readonly strictDynamic?: boolean;
|
|
1330
956
|
}): string;
|
|
1331
|
-
/**
|
|
1332
|
-
* How a directive is merged into an existing CSP header value.
|
|
1333
|
-
*
|
|
1334
|
-
* - `union` (default) — the new sources are added to the existing
|
|
1335
|
-
* directive's sources (deduplicated). An existing `'none'` is
|
|
1336
|
-
* dropped when real sources are added, since `'none'` may not be
|
|
1337
|
-
* combined with other sources.
|
|
1338
|
-
* - `replace` — the new value overwrites the existing directive.
|
|
1339
|
-
*/
|
|
957
|
+
/** `union` (default) adds sources to an existing directive; `replace` overwrites it. */
|
|
1340
958
|
interface CspDirectiveMerge {
|
|
1341
959
|
readonly value: string;
|
|
1342
960
|
readonly mode?: 'union' | 'replace';
|
|
1343
961
|
}
|
|
1344
962
|
/**
|
|
1345
|
-
* Merge directives into an existing `Content-Security-Policy` header
|
|
1346
|
-
*
|
|
1347
|
-
*
|
|
1348
|
-
*
|
|
1349
|
-
* follows CSP3's ASCII-whitespace and first-duplicate-wins rules.
|
|
1350
|
-
*
|
|
1351
|
-
* This is the single CSP-merge implementation shared by every adapter.
|
|
963
|
+
* Merge directives into an existing `Content-Security-Policy` header value.
|
|
964
|
+
* Commas separate policies (that is how `Headers.get()` joins repeated
|
|
965
|
+
* headers); each is merged, because a browser enforces every policy and
|
|
966
|
+
* widening only the last would leave the others blocking.
|
|
1352
967
|
*/
|
|
1353
968
|
declare function mergeCspHeader(existing: string, additions: Readonly<Record<string, string | CspDirectiveMerge>>): string;
|
|
1354
969
|
|
|
1355
970
|
/**
|
|
1356
|
-
*
|
|
1357
|
-
*
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
*
|
|
1361
|
-
*
|
|
1362
|
-
*
|
|
1363
|
-
*
|
|
1364
|
-
*
|
|
1365
|
-
*
|
|
1366
|
-
*
|
|
1367
|
-
*
|
|
1368
|
-
*
|
|
1369
|
-
*
|
|
1370
|
-
*
|
|
1371
|
-
*
|
|
1372
|
-
*
|
|
1373
|
-
*
|
|
1374
|
-
*
|
|
1375
|
-
*
|
|
1376
|
-
*
|
|
1377
|
-
*
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
*/
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
* Which signals count as preview intent. Default: all three.
|
|
1389
|
-
* Restricting this to `['query']` reduces accidental activation, but
|
|
1390
|
-
* an explicit `?preview=true` is still client-controlled and must not
|
|
1391
|
-
* authorize response changes or privileged data access by itself.
|
|
1392
|
-
*/
|
|
1393
|
-
readonly signals?: readonly PreviewSignal[];
|
|
1394
|
-
/**
|
|
1395
|
-
* Query parameters (with values `true` or `1`) that signal preview
|
|
1396
|
-
* intent. Default: `['preview', 'draft', 'livePreview']`.
|
|
1397
|
-
*/
|
|
1398
|
-
readonly queryParams?: readonly string[];
|
|
1399
|
-
/**
|
|
1400
|
-
* Treat `Sec-Fetch-Dest: iframe` as a preview-intent signal. Default `true`.
|
|
1401
|
-
* Legacy alias for excluding `'fetch-dest'` from `signals`.
|
|
1402
|
-
*/
|
|
1403
|
-
readonly checkFetchDest?: boolean;
|
|
1404
|
-
/**
|
|
1405
|
-
* Admin origins whose `Referer` signals preview intent, e.g.
|
|
1406
|
-
* `['https://cms.example.com']`. Referer matching is not HTTP-request
|
|
1407
|
-
* authentication.
|
|
1408
|
-
*/
|
|
1409
|
-
readonly adminOrigins?: readonly string[];
|
|
971
|
+
* The authorized preview context's shape and brand, in the leaf `types` domain
|
|
972
|
+
* so producer and consumers recognise the same context. See ADR 0006.
|
|
973
|
+
*/
|
|
974
|
+
/**
|
|
975
|
+
* The brand key, in the type and on the object.
|
|
976
|
+
*
|
|
977
|
+
* This was a `unique symbol` until 2026-08-30. A `unique symbol` is nominal per
|
|
978
|
+
* *declaration*, and the published entries each carry their own declaration
|
|
979
|
+
* file — so `AuthorizedPreviewContext` from the root barrel was a different
|
|
980
|
+
* type from the one in `./server` or in an adapter, and the documented flow
|
|
981
|
+
* (authorize once, hand the context to the adapter hook and to the draft read)
|
|
982
|
+
* did not type-check in any combination a consumer can write.
|
|
983
|
+
*
|
|
984
|
+
* That is the same trap the runtime brand below already avoids by using
|
|
985
|
+
* `Symbol.for` rather than `Symbol()`; the lesson simply had not reached the
|
|
986
|
+
* type. A shared string literal is identical in every bundle for the same
|
|
987
|
+
* reason a registry symbol is.
|
|
988
|
+
*
|
|
989
|
+
* It is not what makes a context trustworthy. `isAuthorizedPreviewContext`
|
|
990
|
+
* decides that, on the registry symbol and on `Object.isFrozen`, and every
|
|
991
|
+
* consumption point calls it — the adapter policy, the draft read and the
|
|
992
|
+
* binding emitter. Writing this key by hand buys nothing.
|
|
993
|
+
*/
|
|
994
|
+
declare const AUTHORIZED_PREVIEW_BRAND_KEY = "payload-live-preview.authorized-preview-context";
|
|
995
|
+
type PreviewAuthorizationStrategyName = 'payload-session' | 'signed-token' | 'verifier';
|
|
996
|
+
/** What a context is bound to; every field is optional because every strategy binds differently. */
|
|
997
|
+
interface AuthorizedPreviewScope {
|
|
998
|
+
/** The site origin the credential was issued for. */
|
|
999
|
+
readonly audience?: string;
|
|
1000
|
+
/** The request pathname the credential is valid for. */
|
|
1001
|
+
readonly path?: string;
|
|
1002
|
+
readonly locale?: string;
|
|
1410
1003
|
}
|
|
1004
|
+
/** The verdict of a successful `authorizePreviewRequest()`: frozen, branded, produced only there. Carry it, do not rebuild it. */
|
|
1005
|
+
interface AuthorizedPreviewContext {
|
|
1006
|
+
readonly [AUTHORIZED_PREVIEW_BRAND_KEY]: true;
|
|
1007
|
+
readonly strategy: PreviewAuthorizationStrategyName;
|
|
1008
|
+
/** The user id, token subject, or whatever the verifier named. */
|
|
1009
|
+
readonly subject: string | undefined;
|
|
1010
|
+
/** Unix milliseconds. */
|
|
1011
|
+
readonly authorizedAt: number;
|
|
1012
|
+
/** Unix milliseconds, or `undefined` when the strategy does not know. */
|
|
1013
|
+
readonly expiresAt: number | undefined;
|
|
1014
|
+
readonly scope: AuthorizedPreviewScope;
|
|
1015
|
+
/** The minimal request material a draft read forwards to Payload — one verified cookie, never the whole `Cookie` header. */
|
|
1016
|
+
readonly payloadHeaders: Readonly<Record<string, string>>;
|
|
1017
|
+
}
|
|
1018
|
+
/** Whether `value` was produced by `authorizePreviewRequest()`; a copy or JSON round trip is not. */
|
|
1019
|
+
declare function isAuthorizedPreviewContext(value: unknown): value is AuthorizedPreviewContext;
|
|
1020
|
+
|
|
1411
1021
|
/**
|
|
1412
|
-
* The
|
|
1413
|
-
*
|
|
1414
|
-
*
|
|
1022
|
+
* The verdict shape every authorization strategy produces, and the one error
|
|
1023
|
+
* class that is allowed to escape it. Kept apart from the strategies so the
|
|
1024
|
+
* adapters can import the error without pulling in HMAC or session code.
|
|
1415
1025
|
*/
|
|
1416
|
-
|
|
1026
|
+
|
|
1027
|
+
/** Why a request was refused. `'authorized'` is the one non-refusal. */
|
|
1028
|
+
type PreviewAuthorizationOutcome = 'authorized' | 'missing-credential' | 'invalid' | 'expired' | 'wrong-audience' | 'wrong-path' | 'wrong-locale' | 'wrong-purpose' | 'replayed' | 'unavailable';
|
|
1029
|
+
/** The result of `authorizePreviewRequest()`: a verdict, never an exception. */
|
|
1030
|
+
type PreviewAuthorization = {
|
|
1031
|
+
readonly authorized: true;
|
|
1032
|
+
readonly outcome: 'authorized';
|
|
1033
|
+
readonly context: AuthorizedPreviewContext;
|
|
1034
|
+
} | {
|
|
1035
|
+
readonly authorized: false;
|
|
1036
|
+
readonly outcome: Exclude<PreviewAuthorizationOutcome, 'authorized'>;
|
|
1037
|
+
readonly context: null;
|
|
1038
|
+
};
|
|
1039
|
+
/** The request shape every strategy reads: a URL and a header getter. */
|
|
1040
|
+
interface PreviewAuthorizationRequest {
|
|
1417
1041
|
readonly url: string;
|
|
1418
1042
|
readonly headers: {
|
|
1419
1043
|
get(name: string): string | null;
|
|
1420
1044
|
};
|
|
1421
1045
|
}
|
|
1422
|
-
/**
|
|
1423
|
-
* Detect whether `request` carries live-preview intent. See the module
|
|
1424
|
-
* docblock for the exact signals and the required authorization
|
|
1425
|
-
* boundary. This compatibility name does not imply authentication.
|
|
1426
|
-
*/
|
|
1427
|
-
declare function isPreviewRequest(request: PreviewRequestLike, options?: PreviewRequestOptions): boolean;
|
|
1428
1046
|
|
|
1429
1047
|
/**
|
|
1430
|
-
*
|
|
1431
|
-
*
|
|
1432
|
-
* The live-preview runtime patches the DOM *after* the page loads —
|
|
1433
|
-
* the initial server render is the consumer's job. With Payload
|
|
1434
|
-
* drafts enabled, an **authorized** preview should render the draft
|
|
1435
|
-
* version on first load, otherwise editors see stale published
|
|
1436
|
-
* content until their first keystroke.
|
|
1437
|
-
*
|
|
1438
|
-
* These helpers wrap the corresponding REST queries with `draft` and
|
|
1439
|
-
* `depth` options and an explicit nudge to keep `depth` in sync with
|
|
1440
|
-
* the runtime's `mergeDepth` — a mismatch makes populated
|
|
1441
|
-
* relationships degrade to IDs after the first edit.
|
|
1442
|
-
*
|
|
1443
|
-
* These helpers do not authenticate or authorize requests.
|
|
1444
|
-
* `isPreviewRequest()` detects client-controlled preview intent only.
|
|
1445
|
-
* Verify an application-owned server session or short-lived signed
|
|
1446
|
-
* authorization first, and use that one decision to gate `draft`, any
|
|
1447
|
-
* privileged headers, cache bypass, CSP changes, and runtime injection.
|
|
1448
|
-
* Never attach a long-lived service/API key merely because
|
|
1449
|
-
* `isPreviewRequest()` returned `true`.
|
|
1450
|
-
*
|
|
1451
|
-
* Isomorphic and dependency-free: works in Astro frontmatter,
|
|
1452
|
-
* SvelteKit `load`, Next.js server components, or any Node/edge
|
|
1453
|
-
* runtime with `fetch`.
|
|
1454
|
-
*
|
|
1455
|
-
* ```ts
|
|
1456
|
-
* // Astro frontmatter. `verifyAppPreviewSession` is application-owned,
|
|
1457
|
-
* // server-only code; it validates the request and returns only the
|
|
1458
|
-
* // minimal Payload session headers needed for this request.
|
|
1459
|
-
* const hasPreviewIntent = isPreviewRequest(Astro.request);
|
|
1460
|
-
* const authorization = hasPreviewIntent
|
|
1461
|
-
* ? await verifyAppPreviewSession(Astro.request)
|
|
1462
|
-
* : null;
|
|
1463
|
-
* const page = await fetchPreviewDocument<Page>({
|
|
1464
|
-
* serverURL: import.meta.env.PAYLOAD_URL,
|
|
1465
|
-
* collection: 'pages',
|
|
1466
|
-
* where: { slug: { equals: Astro.params.slug } },
|
|
1467
|
-
* draft: authorization !== null,
|
|
1468
|
-
* ...(authorization === null ? {} : { headers: authorization.payloadHeaders }),
|
|
1469
|
-
* });
|
|
1470
|
-
* ```
|
|
1471
|
-
*
|
|
1472
|
-
* @module @preview-fetch
|
|
1048
|
+
* The `payload-session` strategy: forward exactly one named cookie to
|
|
1049
|
+
* `GET <serverURL>/api/<usersSlug>/me` and authorize when a user comes back.
|
|
1473
1050
|
*/
|
|
1474
|
-
|
|
1051
|
+
|
|
1052
|
+
/** A `fetch`-compatible function, injectable for tests. */
|
|
1053
|
+
type FetchLike = (input: string, init: {
|
|
1054
|
+
readonly headers: Record<string, string>;
|
|
1055
|
+
readonly signal: AbortSignal;
|
|
1056
|
+
}) => Promise<{
|
|
1057
|
+
readonly ok: boolean;
|
|
1058
|
+
readonly status: number;
|
|
1059
|
+
json(): Promise<unknown>;
|
|
1060
|
+
}>;
|
|
1061
|
+
interface PayloadSessionStrategy {
|
|
1062
|
+
readonly type: 'payload-session';
|
|
1475
1063
|
/** Payload server origin, e.g. `https://cms.example.com`. */
|
|
1476
1064
|
readonly serverURL: string;
|
|
1477
|
-
/**
|
|
1478
|
-
readonly
|
|
1479
|
-
/**
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
readonly
|
|
1486
|
-
/**
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1065
|
+
/** Auth collection slug; `users` unless the project renamed it. */
|
|
1066
|
+
readonly usersSlug?: string;
|
|
1067
|
+
/** Cookie name; Payload's default is `payload-token`. */
|
|
1068
|
+
readonly cookieName?: string;
|
|
1069
|
+
/** Upper bound for the `/me` round trip. Below 250 ms is raised to 250 ms; default 3000. */
|
|
1070
|
+
readonly timeoutMs?: number;
|
|
1071
|
+
/** Longest cookie value accepted; longer ones are refused as `invalid`. Default 4096. */
|
|
1072
|
+
readonly maxCookieLength?: number;
|
|
1073
|
+
readonly fetch?: FetchLike;
|
|
1074
|
+
/** Clock, Unix milliseconds. Injectable for tests. */
|
|
1075
|
+
readonly now?: () => number;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* The `signed-token` strategy: a short-lived HMAC-SHA256 token minted by the
|
|
1080
|
+
* Payload side and verified by the site. Format and bindings: ADR 0006 §3.
|
|
1081
|
+
*/
|
|
1082
|
+
|
|
1083
|
+
/** The subset of Web Crypto the token strategy needs, injectable for environments that expose it elsewhere. */
|
|
1084
|
+
interface SubtleCryptoLike {
|
|
1085
|
+
readonly subtle: {
|
|
1086
|
+
importKey(format: 'raw', keyData: Uint8Array, algorithm: {
|
|
1087
|
+
readonly name: 'HMAC';
|
|
1088
|
+
readonly hash: 'SHA-256';
|
|
1089
|
+
}, extractable: false, keyUsages: readonly ('sign' | 'verify')[]): Promise<CryptoKey>;
|
|
1090
|
+
sign(algorithm: 'HMAC', key: CryptoKey, data: Uint8Array): Promise<ArrayBuffer>;
|
|
1091
|
+
verify(algorithm: 'HMAC', key: CryptoKey, signature: Uint8Array, data: Uint8Array): Promise<boolean>;
|
|
1092
|
+
};
|
|
1093
|
+
getRandomValues<T extends Uint8Array>(array: T): T;
|
|
1094
|
+
}
|
|
1095
|
+
/** Where a signed token is read from on the request. */
|
|
1096
|
+
type PreviewTokenTransport = {
|
|
1097
|
+
readonly kind: 'query';
|
|
1098
|
+
readonly param?: string;
|
|
1099
|
+
} | {
|
|
1100
|
+
readonly kind: 'header';
|
|
1101
|
+
readonly name?: string;
|
|
1102
|
+
};
|
|
1103
|
+
/**
|
|
1104
|
+
* Optional replay store. Not shipped: a per-process memory is useless behind
|
|
1105
|
+
* a load balancer, so the store is a deployment decision.
|
|
1106
|
+
*/
|
|
1107
|
+
interface PreviewTokenReplayStore {
|
|
1108
|
+
isUsed(id: string): Promise<boolean> | boolean;
|
|
1109
|
+
markUsed(id: string, expiresAt: number): Promise<void> | void;
|
|
1110
|
+
}
|
|
1111
|
+
interface SignedTokenStrategy {
|
|
1112
|
+
readonly type: 'signed-token';
|
|
1113
|
+
/** Shared secret, at least 32 bytes; shorter ones are refused on first use. */
|
|
1114
|
+
readonly secret: string | Uint8Array;
|
|
1115
|
+
/** The site origin tokens must name, e.g. `https://www.example.com`. */
|
|
1116
|
+
readonly audience: string;
|
|
1117
|
+
/** Purpose string tokens must carry. Default `live-preview`. */
|
|
1118
|
+
readonly purpose?: string;
|
|
1119
|
+
/** Default: query parameter `previewToken`. */
|
|
1120
|
+
readonly transport?: PreviewTokenTransport;
|
|
1121
|
+
/** Resolve the request's locale for the `loc` binding. Unset: tokens with `loc` are refused as `wrong-locale`. */
|
|
1122
|
+
readonly locale?: (request: PreviewAuthorizationRequest) => string | undefined;
|
|
1123
|
+
readonly replay?: PreviewTokenReplayStore;
|
|
1124
|
+
readonly crypto?: SubtleCryptoLike;
|
|
1125
|
+
readonly now?: () => number;
|
|
1126
|
+
}
|
|
1127
|
+
/** What `issuePreviewToken()` binds a token to. */
|
|
1128
|
+
interface PreviewTokenClaims {
|
|
1129
|
+
/** The site origin the token is for. */
|
|
1130
|
+
readonly audience: string;
|
|
1131
|
+
/** Request pathname the token opens. Recommended; omit only for a single-page preview. */
|
|
1132
|
+
readonly path?: string;
|
|
1493
1133
|
readonly locale?: string;
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
* service/API key based on a preview-intent signal.
|
|
1499
|
-
*/
|
|
1500
|
-
readonly headers?: Readonly<Record<string, string>>;
|
|
1501
|
-
/** Injectable fetch implementation (tests, custom agents). */
|
|
1502
|
-
readonly fetchFn?: typeof fetch;
|
|
1503
|
-
}
|
|
1504
|
-
/** Payload `where` clause — kept loose on purpose (server validates). */
|
|
1505
|
-
type PreviewWhere = Readonly<Record<string, unknown>>;
|
|
1506
|
-
interface FetchPreviewDocumentOptions extends PreviewFetchBaseOptions {
|
|
1507
|
-
/** Collection slug. */
|
|
1508
|
-
readonly collection: string;
|
|
1509
|
-
/** Fetch a single document by id. Mutually exclusive with `where`. */
|
|
1510
|
-
readonly id?: string | number;
|
|
1511
|
-
/**
|
|
1512
|
-
* Fetch the first document matching this Payload `where` clause,
|
|
1513
|
-
* e.g. `{ slug: { equals: 'about' } }`.
|
|
1514
|
-
*/
|
|
1515
|
-
readonly where?: PreviewWhere;
|
|
1134
|
+
readonly subject?: string;
|
|
1135
|
+
readonly purpose?: string;
|
|
1136
|
+
/** Lifetime in milliseconds. Default ten minutes; capped at one hour. */
|
|
1137
|
+
readonly ttlMs?: number;
|
|
1516
1138
|
}
|
|
1517
|
-
interface
|
|
1518
|
-
|
|
1519
|
-
readonly
|
|
1139
|
+
interface IssuePreviewTokenOptions {
|
|
1140
|
+
readonly secret: string | Uint8Array;
|
|
1141
|
+
readonly crypto?: SubtleCryptoLike;
|
|
1142
|
+
readonly now?: () => number;
|
|
1520
1143
|
}
|
|
1144
|
+
/** Mint a preview token on the Payload side; pair with `buildLivePreviewUrl`. */
|
|
1145
|
+
declare function issuePreviewToken(claims: PreviewTokenClaims, options: IssuePreviewTokenOptions): Promise<string>;
|
|
1146
|
+
|
|
1521
1147
|
/**
|
|
1522
|
-
*
|
|
1523
|
-
*
|
|
1524
|
-
*
|
|
1525
|
-
*
|
|
1148
|
+
* Authorized preview context: the one verdict every privileged preview
|
|
1149
|
+
* decision is keyed on. Intent (`?preview=true`, an iframe destination, a
|
|
1150
|
+
* referer) is client-chosen; this turns a session, a signed token or the
|
|
1151
|
+
* application's own verifier into a branded context. Threat model: ADR 0006.
|
|
1526
1152
|
*/
|
|
1527
|
-
|
|
1153
|
+
|
|
1154
|
+
/** Claims a consumer verifier returns for a request it accepts. */
|
|
1155
|
+
interface PreviewVerifierClaims {
|
|
1156
|
+
readonly subject?: string;
|
|
1157
|
+
readonly expiresAt?: number;
|
|
1158
|
+
readonly scope?: AuthorizedPreviewScope;
|
|
1159
|
+
readonly payloadHeaders?: Readonly<Record<string, string>>;
|
|
1160
|
+
}
|
|
1161
|
+
/** The application's own verification: claims authorize, `null` refuses, a throw is `unavailable`. */
|
|
1162
|
+
interface VerifierStrategy {
|
|
1163
|
+
readonly type: 'verifier';
|
|
1164
|
+
readonly verify: (request: PreviewAuthorizationRequest) => Promise<PreviewVerifierClaims | null> | PreviewVerifierClaims | null;
|
|
1165
|
+
readonly now?: () => number;
|
|
1166
|
+
}
|
|
1167
|
+
type PreviewAuthorizationStrategy = PayloadSessionStrategy | SignedTokenStrategy | VerifierStrategy;
|
|
1168
|
+
/** Decide whether `request` is an authorized preview; refusals resolve to `{ authorized: false }` and only a `PreviewConfigurationError` throws. */
|
|
1169
|
+
declare function authorizePreviewRequest(request: PreviewAuthorizationRequest, strategy: PreviewAuthorizationStrategy): Promise<PreviewAuthorization>;
|
|
1170
|
+
|
|
1528
1171
|
/**
|
|
1529
|
-
*
|
|
1530
|
-
*
|
|
1172
|
+
* Trusted Types: every HTML sink routes through one policy, `payload-live-preview`,
|
|
1173
|
+
* whose `createHTML` is the identity — what reaches a sink is already sanitized
|
|
1174
|
+
* or escaped. Without the API, strings pass through untouched.
|
|
1531
1175
|
*/
|
|
1532
|
-
|
|
1176
|
+
/** The subset of the Trusted Types API this module needs. */
|
|
1177
|
+
interface TrustedHtmlPolicyLike {
|
|
1178
|
+
createHTML(input: string): unknown;
|
|
1179
|
+
}
|
|
1180
|
+
declare const TRUSTED_TYPES_POLICY_NAME = "payload-live-preview";
|
|
1181
|
+
/**
|
|
1182
|
+
* Use `policy` for every sink, `null` to assign plain strings, `undefined` to
|
|
1183
|
+
* return to the auto-created package policy.
|
|
1184
|
+
*/
|
|
1185
|
+
declare function setTrustedTypesPolicy(policy: TrustedHtmlPolicyLike | null | undefined): void;
|
|
1186
|
+
/** `html` as a `TrustedHTML` when a policy exists, else the string. Typed `string` for `innerHTML`. */
|
|
1187
|
+
declare function trustedHtml(html: string): string;
|
|
1533
1188
|
|
|
1534
1189
|
/**
|
|
1535
|
-
*
|
|
1536
|
-
*
|
|
1190
|
+
* Server-side preview-intent detection. Every signal here is client-controlled:
|
|
1191
|
+
* a positive result is a delivery hint, never proof of identity or permission.
|
|
1192
|
+
* Verify with `authorizePreviewRequest()` before anything privileged.
|
|
1537
1193
|
*/
|
|
1194
|
+
/** `query`: `?preview=true`; `fetch-dest`: `Sec-Fetch-Dest: iframe`; `referer`: an admin-origin Referer. */
|
|
1195
|
+
type PreviewSignal = 'query' | 'fetch-dest' | 'referer';
|
|
1196
|
+
interface PreviewRequestOptions {
|
|
1197
|
+
/** Which signals count. Default: all three. `['query']` is the adapters' 2.0 default. */
|
|
1198
|
+
readonly signals?: readonly PreviewSignal[];
|
|
1199
|
+
/** Query parameters (value `true` or `1`) that signal intent. Default `['preview', 'draft', 'livePreview']`. */
|
|
1200
|
+
readonly queryParams?: readonly string[];
|
|
1201
|
+
/** Admin origins whose Referer signals intent, e.g. `['https://cms.example.com']`. */
|
|
1202
|
+
readonly adminOrigins?: readonly string[];
|
|
1203
|
+
}
|
|
1204
|
+
/** The request surface the predicate reads; `Request` satisfies it, Nitro/H3 events need a shim. */
|
|
1205
|
+
interface PreviewRequestLike {
|
|
1206
|
+
readonly url: string;
|
|
1207
|
+
readonly headers: {
|
|
1208
|
+
get(name: string): string | null;
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
/** Whether `request` carries live-preview intent. Intent, not authorization. */
|
|
1212
|
+
declare function hasPreviewIntent(request: PreviewRequestLike, options?: PreviewRequestOptions): boolean;
|
|
1213
|
+
|
|
1538
1214
|
interface LexicalNode {
|
|
1539
1215
|
readonly type: string;
|
|
1540
1216
|
readonly version?: number;
|
|
@@ -1545,9 +1221,6 @@ interface LexicalNode {
|
|
|
1545
1221
|
readonly text?: string;
|
|
1546
1222
|
readonly [extra: string]: unknown;
|
|
1547
1223
|
}
|
|
1548
|
-
/**
|
|
1549
|
-
* Root of a Lexical document.
|
|
1550
|
-
*/
|
|
1551
1224
|
interface LexicalRoot {
|
|
1552
1225
|
readonly root: {
|
|
1553
1226
|
readonly type?: string;
|
|
@@ -1560,107 +1233,44 @@ interface LexicalRoot {
|
|
|
1560
1233
|
}
|
|
1561
1234
|
|
|
1562
1235
|
/**
|
|
1563
|
-
*
|
|
1564
|
-
*
|
|
1565
|
-
* Walks the document tree, dispatching each node to the renderer
|
|
1566
|
-
* registered for its `type`. Unknown nodes are handled gracefully:
|
|
1567
|
-
* children are rendered (so the user sees content), the wrapper is
|
|
1568
|
-
* skipped.
|
|
1569
|
-
*
|
|
1570
|
-
* @module @lexical/render
|
|
1236
|
+
* Lexical → HTML. Each node dispatches to the renderer registered for its
|
|
1237
|
+
* `type`; an unknown node renders its children so content is never lost.
|
|
1571
1238
|
*/
|
|
1572
1239
|
|
|
1573
1240
|
interface LexicalRenderOptions {
|
|
1574
|
-
/**
|
|
1575
|
-
* When `true` (default), the resulting HTML is passed through the
|
|
1576
|
-
* isomorphic `sanitizeHtml()` for defence-in-depth.
|
|
1577
|
-
*/
|
|
1241
|
+
/** Pass the result through `sanitizeHtml()` (default `true`). */
|
|
1578
1242
|
readonly sanitize?: boolean;
|
|
1579
1243
|
}
|
|
1580
|
-
/**
|
|
1581
|
-
* Identify whether a value looks like a Lexical root payload.
|
|
1582
|
-
*/
|
|
1583
1244
|
declare function isLexicalContent(value: unknown): value is LexicalRoot;
|
|
1584
|
-
/**
|
|
1585
|
-
* Convert a Lexical root payload to an HTML string. By default the
|
|
1586
|
-
* result is sanitised through `sanitizeHtml`. The renderer is
|
|
1587
|
-
* security-first; per-node renderers already escape text and validate
|
|
1588
|
-
* URLs, but the final sanitiser closes any gap.
|
|
1589
|
-
*/
|
|
1245
|
+
/** Render a Lexical document to HTML; without `setSanitizerDocument()` the result is unsanitised and warns once. */
|
|
1590
1246
|
declare function lexicalToHtml(content: LexicalRoot, options?: LexicalRenderOptions): string;
|
|
1591
|
-
/**
|
|
1592
|
-
* Extract the plain text content of a Lexical document.
|
|
1593
|
-
*
|
|
1594
|
-
* Paragraphs are separated by `\n`. Used by the text/textarea renderer
|
|
1595
|
-
* when a rich-text value is bound to a non-rich-text element.
|
|
1596
|
-
*/
|
|
1247
|
+
/** Plain text of a document, paragraphs separated by `\n`. */
|
|
1597
1248
|
declare function lexicalToPlainText(content: LexicalRoot): string;
|
|
1598
1249
|
|
|
1599
|
-
/**
|
|
1600
|
-
* Block-renderer sub-registry.
|
|
1601
|
-
*
|
|
1602
|
-
* Payload's BlocksFeature emits Lexical `block` nodes with a
|
|
1603
|
-
* `blockType` slug. The default `block`-node renderer (in
|
|
1604
|
-
* `../nodes/block.ts`) only emits a generic
|
|
1605
|
-
* `<div data-block-type="…">` — useful as a hook, but unhelpful when
|
|
1606
|
-
* the consumer wants out-of-the-box rendering for common block types.
|
|
1607
|
-
*
|
|
1608
|
-
* This registry maps `blockType` → renderer. Default renderers ship
|
|
1609
|
-
* for the most common Payload block patterns (callout, image,
|
|
1610
|
-
* video, code, cta). Custom slugs override defaults via
|
|
1611
|
-
* `registerBlockRenderer(slug, renderer)`.
|
|
1612
|
-
*
|
|
1613
|
-
* @module @lexical/blocks/registry
|
|
1614
|
-
*/
|
|
1250
|
+
/** Block-renderer registry keyed by Payload block slug; consulted by the `block` and `inlineBlock` nodes. */
|
|
1615
1251
|
|
|
1616
|
-
/**
|
|
1617
|
-
* Context passed to a block renderer. Includes the recursive
|
|
1618
|
-
* `renderChildren` function so blocks that house nested Lexical
|
|
1619
|
-
* content can delegate back to the main renderer.
|
|
1620
|
-
*/
|
|
1621
1252
|
interface BlockRenderContext {
|
|
1622
|
-
/**
|
|
1253
|
+
/** Render nested Lexical content through the node registry. */
|
|
1623
1254
|
readonly renderChildren: (children: readonly LexicalNode[]) => string;
|
|
1624
1255
|
}
|
|
1625
|
-
/**
|
|
1626
|
-
* Renderer signature. Receives the block node *and* its already-read
|
|
1627
|
-
* `fields` payload (the parser pulls these out so individual block
|
|
1628
|
-
* renderers don't have to reach into `node['fields']` themselves).
|
|
1629
|
-
*/
|
|
1256
|
+
/** Receives the block's `fields` (with `blockType`) and returns HTML. */
|
|
1630
1257
|
type BlockRenderer = (fields: Record<string, unknown>, context: BlockRenderContext) => string;
|
|
1631
1258
|
/** Register or replace the renderer for `blockType`. */
|
|
1632
1259
|
declare function registerBlockRenderer(blockType: string, renderer: BlockRenderer): void;
|
|
1633
1260
|
|
|
1634
1261
|
/**
|
|
1635
|
-
* `highlight` plugin
|
|
1636
|
-
*
|
|
1637
|
-
*
|
|
1638
|
-
* Respects `prefers-reduced-motion`. Adds at most one style tag per
|
|
1639
|
-
* document.
|
|
1640
|
-
*
|
|
1641
|
-
* @module @plugins/built-in/highlight
|
|
1262
|
+
* `highlight` plugin: flashes an outline on updated elements. Respects
|
|
1263
|
+
* `prefers-reduced-motion`; the stylesheet is shared per document across
|
|
1264
|
+
* clients through a lease (ADR 0002).
|
|
1642
1265
|
*/
|
|
1643
1266
|
|
|
1644
1267
|
declare const highlightPlugin: LivePreviewPlugin;
|
|
1645
1268
|
|
|
1646
|
-
/**
|
|
1647
|
-
* `debug` plugin — logs every lifecycle event via the plugin context.
|
|
1648
|
-
*
|
|
1649
|
-
* @module @plugins/built-in/debug
|
|
1650
|
-
*/
|
|
1269
|
+
/** `debug` plugin: logs every lifecycle event through the plugin context. */
|
|
1651
1270
|
|
|
1652
1271
|
declare const debugPlugin: LivePreviewPlugin;
|
|
1653
1272
|
|
|
1654
|
-
/**
|
|
1655
|
-
* `analytics` plugin — accumulates summary statistics about the
|
|
1656
|
-
* update stream. Useful for performance dashboards.
|
|
1657
|
-
*
|
|
1658
|
-
* The plugin owns its own state (no module singleton) and exposes it
|
|
1659
|
-
* via the `getStats` factory passed through the plugin's `init` hook
|
|
1660
|
-
* by closing over a local object.
|
|
1661
|
-
*
|
|
1662
|
-
* @module @plugins/built-in/analytics
|
|
1663
|
-
*/
|
|
1273
|
+
/** `analytics` plugin: running totals over the update stream, read through `getStats()`. */
|
|
1664
1274
|
|
|
1665
1275
|
interface AnalyticsSnapshot {
|
|
1666
1276
|
readonly updateCount: number;
|
|
@@ -1673,281 +1283,100 @@ declare function createAnalyticsPlugin(): LivePreviewPlugin & {
|
|
|
1673
1283
|
};
|
|
1674
1284
|
|
|
1675
1285
|
/**
|
|
1676
|
-
* `documentSave` plugin
|
|
1677
|
-
*
|
|
1678
|
-
*
|
|
1679
|
-
*
|
|
1680
|
-
* - `'reload'` → reload the page, **preserving the scroll
|
|
1681
|
-
* position** across the reload (the
|
|
1682
|
-
* framework-agnostic analog of Payload's
|
|
1683
|
-
* `RefreshRouteOnSave`).
|
|
1684
|
-
* - `'revalidate'` → POST to a revalidation endpoint (Astro,
|
|
1685
|
-
* Next.js convention) so SSR caches refresh
|
|
1686
|
-
* without losing client state.
|
|
1687
|
-
* - `'fetch'` → custom async handler supplied by the user.
|
|
1688
|
-
*
|
|
1689
|
-
* Each strategy is intentionally narrow so consumers can compose them
|
|
1690
|
-
* — e.g. revalidate, then if revalidation fails, fall back to reload.
|
|
1691
|
-
*
|
|
1692
|
-
* @module @plugins/built-in/document-save
|
|
1286
|
+
* `documentSave` plugin: what to do when Payload saves the document.
|
|
1287
|
+
* `'silent'` (default) emits only; `'reload'` reloads and restores the scroll
|
|
1288
|
+
* position; `'revalidate'` POSTs to a revalidation endpoint; `'fetch'` runs
|
|
1289
|
+
* a custom handler.
|
|
1693
1290
|
*/
|
|
1694
1291
|
|
|
1695
1292
|
type DocumentSaveStrategy = 'silent' | 'reload' | 'revalidate' | 'fetch';
|
|
1696
|
-
/**
|
|
1697
|
-
* A fetch strategy handler. The optional parameter preserves calls through
|
|
1698
|
-
* pre-1.0.4 option objects (`options.handler?.()`), while method bivariance
|
|
1699
|
-
* also accepts explicitly signal-aware handlers. The built-in plugin always
|
|
1700
|
-
* supplies a live `AbortSignal`.
|
|
1701
|
-
*/
|
|
1293
|
+
/** Handler for the `fetch` strategy; the signal aborts when the plugin is removed. */
|
|
1702
1294
|
type DocumentSaveHandler = {
|
|
1703
1295
|
bivarianceHack(signal?: AbortSignal): void | Promise<void>;
|
|
1704
1296
|
}['bivarianceHack'];
|
|
1705
1297
|
interface DocumentSavePluginOptions {
|
|
1706
|
-
/**
|
|
1298
|
+
/** Default `'silent'`. */
|
|
1707
1299
|
readonly strategy?: DocumentSaveStrategy;
|
|
1708
1300
|
/**
|
|
1709
|
-
*
|
|
1710
|
-
*
|
|
1301
|
+
* `'revalidate'` endpoint, default `/api/revalidate`; the body is
|
|
1302
|
+
* `{ source: 'payload-live-preview' }`. The request is a plain `fetch` from
|
|
1303
|
+
* the preview page: cookies travel with it, there is no CSRF token, and a
|
|
1304
|
+
* bearer secret in `revalidateHeaders` would sit in page JavaScript. The
|
|
1305
|
+
* endpoint must authorise on its own (session cookie, same-origin check).
|
|
1711
1306
|
*/
|
|
1712
1307
|
readonly revalidateUrl?: string;
|
|
1713
|
-
/**
|
|
1714
|
-
* For `'revalidate'`: extra headers (e.g., `Authorization: Bearer …`).
|
|
1715
|
-
* The `Content-Type: application/json` header is set automatically.
|
|
1716
|
-
*/
|
|
1308
|
+
/** Extra request headers for `'revalidate'`; `Content-Type` is set already. */
|
|
1717
1309
|
readonly revalidateHeaders?: Readonly<Record<string, string>>;
|
|
1718
|
-
/**
|
|
1719
|
-
* For `'fetch'`: async handler called on each event. The plugin
|
|
1720
|
-
* awaits it; rejections are logged but don't crash the runtime. The signal
|
|
1721
|
-
* aborts when the plugin is removed. Existing zero-argument handlers remain
|
|
1722
|
-
* compatible because they may ignore the argument.
|
|
1723
|
-
*/
|
|
1310
|
+
/** `'fetch'` handler; rejections are logged, never thrown. */
|
|
1724
1311
|
readonly handler?: DocumentSaveHandler;
|
|
1725
|
-
/**
|
|
1726
|
-
* Optional fallback strategy when `'revalidate'` POST fails (network
|
|
1727
|
-
* error or non-2xx). Set to `'reload'` to hard-refresh the page when
|
|
1728
|
-
* SSR-cache invalidation can't be confirmed.
|
|
1729
|
-
*/
|
|
1312
|
+
/** `'reload'` hard-refreshes when the revalidate request fails or returns non-2xx. */
|
|
1730
1313
|
readonly onRevalidateFailure?: 'silent' | 'reload';
|
|
1731
1314
|
}
|
|
1732
|
-
/**
|
|
1733
|
-
* Build the plugin. Pass it to `client.use(documentSavePlugin({ … }))`.
|
|
1734
|
-
*/
|
|
1735
1315
|
declare function documentSavePlugin(options?: DocumentSavePluginOptions): LivePreviewPlugin;
|
|
1736
1316
|
|
|
1737
1317
|
/**
|
|
1738
|
-
*
|
|
1739
|
-
*
|
|
1740
|
-
*
|
|
1741
|
-
* running inside an iframe, in a popup, in development mode, and
|
|
1742
|
-
* whether environment variables are accessible. None of these checks
|
|
1743
|
-
* is dramatic on its own, but having a single, tested module avoids
|
|
1744
|
-
* the duplicated, subtly-different `typeof process` snippets that
|
|
1745
|
-
* accumulated in the legacy implementation.
|
|
1746
|
-
*
|
|
1747
|
-
* @module @detection/environment
|
|
1748
|
-
*/
|
|
1749
|
-
/**
|
|
1750
|
-
* Returns `true` when the current window is inside an iframe.
|
|
1751
|
-
*
|
|
1752
|
-
* Uses a try/catch around `window.self !== window.top` because the
|
|
1753
|
-
* comparison throws in cross-origin iframes — and that very throw is
|
|
1754
|
-
* itself a definitive "yes, I am in an iframe" signal.
|
|
1318
|
+
* Where the runtime is running: framed, opened, or on a developer's machine.
|
|
1319
|
+
* Nothing reads bundler-injected variables; a Vite consumer passes
|
|
1320
|
+
* `import.meta.env.X` explicitly (docs/astro.md).
|
|
1755
1321
|
*/
|
|
1756
1322
|
declare function isInIframe(): boolean;
|
|
1757
|
-
/**
|
|
1758
|
-
* Returns `true` when the current window was opened via `window.open()`
|
|
1759
|
-
* and the opener reference is still accessible.
|
|
1760
|
-
*/
|
|
1761
1323
|
declare function isInPopup(): boolean;
|
|
1762
|
-
/**
|
|
1763
|
-
* Returns `true` when running in a preview-capable context: iframe or
|
|
1764
|
-
* popup. Anything else (top-level navigation) should skip live preview
|
|
1765
|
-
* initialisation entirely.
|
|
1766
|
-
*/
|
|
1324
|
+
/** An iframe or a popup; top-level navigation never starts the preview. */
|
|
1767
1325
|
declare function isInPreviewContext(): boolean;
|
|
1768
|
-
/**
|
|
1769
|
-
* Returns `true` when the runtime appears to be in development mode.
|
|
1770
|
-
*
|
|
1771
|
-
* Detection order:
|
|
1772
|
-
* 1. `process.env.NODE_ENV !== 'production'`
|
|
1773
|
-
* 2. `import.meta.env.DEV === true` (Vite / Astro / SvelteKit)
|
|
1774
|
-
* 3. `window.location.hostname` is `localhost`/`127.0.0.1`
|
|
1775
|
-
*
|
|
1776
|
-
* Returns `false` when none of those are available.
|
|
1777
|
-
*/
|
|
1326
|
+
/** Under Node, `NODE_ENV !== 'production'`; in a browser the hostname, which is the only signal a bundle has. */
|
|
1778
1327
|
declare function isDevMode(): boolean;
|
|
1779
1328
|
|
|
1780
1329
|
/**
|
|
1781
|
-
*
|
|
1782
|
-
*
|
|
1783
|
-
*
|
|
1784
|
-
* matcher used by the message bus. Three sources, in precedence order:
|
|
1785
|
-
*
|
|
1786
|
-
* 1. Explicit origins passed via configuration. Always trusted.
|
|
1787
|
-
* 2. `document.referrer` — **only when no explicit origins are
|
|
1788
|
-
* configured**. The browser writes the embedder's origin here
|
|
1789
|
-
* when the iframe loads; using it as a fallback gives a
|
|
1790
|
-
* zero-config path, but it must never widen an explicit
|
|
1791
|
-
* allow-list (the referrer is whoever framed the page, which in
|
|
1792
|
-
* an attack is the attacker).
|
|
1793
|
-
* 3. Localhost pattern (any port on `localhost` / `127.0.0.1`),
|
|
1794
|
-
* enabled when development mode is detected. Replaces the
|
|
1795
|
-
* legacy hand-rolled port list.
|
|
1796
|
-
*
|
|
1797
|
-
* Once the parent's origin is confirmed by an accepted data-bearing
|
|
1798
|
-
* update, callers can call `lockOrigin()` to drop every other candidate,
|
|
1799
|
-
* making subsequent matches exact. A data-less `ready` handshake does
|
|
1800
|
-
* not lock the detector.
|
|
1801
|
-
*
|
|
1802
|
-
* @module @detection/origin
|
|
1330
|
+
* Which admin origins the runtime trusts: explicit ones always, the
|
|
1331
|
+
* `document.referrer` origin only when none is configured, any localhost port
|
|
1332
|
+
* in development. See docs/security.md §1.
|
|
1803
1333
|
*/
|
|
1804
1334
|
interface OriginDetectorOptions {
|
|
1805
|
-
/** Explicit
|
|
1335
|
+
/** Explicit origins to trust. Always allowed. */
|
|
1806
1336
|
readonly additionalOrigins?: readonly string[];
|
|
1807
|
-
/** Set to `false` to
|
|
1337
|
+
/** Set to `false` to ignore `document.referrer`. */
|
|
1808
1338
|
readonly enableReferrerDetection?: boolean;
|
|
1809
|
-
/** Set to `false` to
|
|
1339
|
+
/** Set to `false` to ignore the localhost pattern even in development. */
|
|
1810
1340
|
readonly enableLocalhostMatching?: boolean;
|
|
1811
|
-
/**
|
|
1812
|
-
* Override the dev-mode detection. When `undefined`, dev mode is
|
|
1813
|
-
* inferred from the runtime (see `isDevMode`).
|
|
1814
|
-
*/
|
|
1341
|
+
/** Override dev-mode detection. */
|
|
1815
1342
|
readonly forceDevMode?: boolean;
|
|
1816
1343
|
/** Override `document.referrer` for tests. */
|
|
1817
1344
|
readonly referrer?: string;
|
|
1818
1345
|
}
|
|
1819
|
-
/**
|
|
1820
|
-
* Holds the resolved origin policy and exposes a matcher.
|
|
1821
|
-
*
|
|
1822
|
-
* The detector is stateful: it starts with the union of all candidate
|
|
1823
|
-
* sources, then narrows down to a single, verified origin once the
|
|
1824
|
-
* lifecycle calls `lockOrigin()` for its first accepted data-bearing
|
|
1825
|
-
* update.
|
|
1826
|
-
*/
|
|
1346
|
+
/** The trusted-origin policy: the union of the candidate sources, narrowed to one by `lockOrigin()`. */
|
|
1827
1347
|
declare class OriginDetector {
|
|
1828
1348
|
#private;
|
|
1829
1349
|
constructor(options?: OriginDetectorOptions);
|
|
1830
|
-
/**
|
|
1831
|
-
* Returns `true` when `origin` is trusted under the current policy.
|
|
1832
|
-
*
|
|
1833
|
-
* If an origin has been locked, only that exact value matches.
|
|
1834
|
-
* Otherwise explicit origins and (in dev mode) localhost-pattern
|
|
1835
|
-
* matches are accepted. The referrer-derived origin is a **fallback,
|
|
1836
|
-
* not a union member**: it only counts when NO explicit origins are
|
|
1837
|
-
* configured. `document.referrer` always names whoever actually
|
|
1838
|
-
* framed the page — if it were unioned with the explicit list, any
|
|
1839
|
-
* site embedding the page in an iframe would become a trusted
|
|
1840
|
-
* postMessage sender despite the consumer having pinned the admin
|
|
1841
|
-
* origin explicitly.
|
|
1842
|
-
*/
|
|
1350
|
+
/** Whether `origin` is trusted: the locked origin alone once locked, otherwise the policy above. */
|
|
1843
1351
|
matches(origin: string): boolean;
|
|
1844
|
-
/**
|
|
1845
|
-
* Narrow the trusted set to a single origin after an accepted
|
|
1846
|
-
* data-bearing update. Subsequent calls to `matches()` only allow
|
|
1847
|
-
* `origin`.
|
|
1848
|
-
*
|
|
1849
|
-
* Returns `true` if locking succeeded; `false` if the candidate is
|
|
1850
|
-
* not currently considered trusted (which would defeat the purpose
|
|
1851
|
-
* of locking).
|
|
1852
|
-
*/
|
|
1352
|
+
/** Narrow trust to `origin`; refused (`false`) when it is not currently trusted. */
|
|
1853
1353
|
lockOrigin(origin: string): boolean;
|
|
1854
|
-
/**
|
|
1855
|
-
* Release the origin lock so any allow-listed origin is trusted
|
|
1856
|
-
* again until the next accepted data-bearing update. The lifecycle
|
|
1857
|
-
* calls this on heartbeat timeout — after the trusted parent disappears,
|
|
1858
|
-
* a different (still-allowed) origin should be free to reconnect.
|
|
1859
|
-
*
|
|
1860
|
-
* Returns the previously-locked origin, or `undefined` when nothing
|
|
1861
|
-
* was locked.
|
|
1862
|
-
*/
|
|
1354
|
+
/** Release the lock (heartbeat timeout) so another allow-listed origin may reconnect. Returns the previous lock. */
|
|
1863
1355
|
unlockOrigin(): string | undefined;
|
|
1864
|
-
/** The currently locked origin, or `undefined`. */
|
|
1865
1356
|
get lockedOrigin(): string | undefined;
|
|
1866
|
-
/**
|
|
1867
|
-
* Snapshot of every origin currently allowed by the policy. Used by
|
|
1868
|
-
* the message bus to broadcast `ready` messages.
|
|
1869
|
-
*
|
|
1870
|
-
* Localhost pattern matches are expanded into the common dev ports
|
|
1871
|
-
* so the handshake can find a Payload running on its usual port.
|
|
1872
|
-
*/
|
|
1357
|
+
/** Every origin the `ready` handshake is broadcast to; the localhost pattern expands to the common dev ports. */
|
|
1873
1358
|
enumerate(): string[];
|
|
1874
|
-
/**
|
|
1875
|
-
* Reports whether `document.referrer` contributed at least one
|
|
1876
|
-
* origin. Useful for surfacing a console warning when running in
|
|
1877
|
-
* production without an explicit origin and the referrer is absent
|
|
1878
|
-
* (because of a `Referrer-Policy: no-referrer` header).
|
|
1879
|
-
*/
|
|
1359
|
+
/** Whether `document.referrer` contributed an origin (absent under `Referrer-Policy: no-referrer`). */
|
|
1880
1360
|
get referrerWasAvailable(): boolean;
|
|
1881
|
-
/**
|
|
1882
|
-
* Indicates whether the detector is operating in a "production-without-
|
|
1883
|
-
* explicit-origin" configuration. When `true`, the host should log a
|
|
1884
|
-
* loud warning — silent connection failure is the worst outcome.
|
|
1885
|
-
*/
|
|
1361
|
+
/** Framed, outside development, with no trusted origin at all: the host should warn (LP0101). */
|
|
1886
1362
|
get isProductionUnconfigured(): boolean;
|
|
1887
|
-
/**
|
|
1888
|
-
* `true` when the referrer is the ONLY trust source (no explicit
|
|
1889
|
-
* origins, no localhost matching). In that mode any site that frames
|
|
1890
|
-
* the page becomes a trusted postMessage sender — `document.referrer`
|
|
1891
|
-
* always names the actual embedder. Content is still sanitized, but
|
|
1892
|
-
* the origin allow-list no longer restricts who may drive it. Hosts
|
|
1893
|
-
* should surface a warning suggesting explicit `allowedOrigins` and
|
|
1894
|
-
* a `frame-ancestors` policy in production.
|
|
1895
|
-
*/
|
|
1363
|
+
/** The referrer is the only trust source, so any framing site may drive the preview (LP0102). */
|
|
1896
1364
|
get isReferrerOnlyTrust(): boolean;
|
|
1897
1365
|
}
|
|
1898
1366
|
|
|
1899
|
-
/**
|
|
1900
|
-
* Locale detection.
|
|
1901
|
-
*
|
|
1902
|
-
* The runtime needs a sensible default locale for date/number renders
|
|
1903
|
-
* before Payload tells us the active locale. Order of precedence:
|
|
1904
|
-
*
|
|
1905
|
-
* 1. The `lang` attribute on `<html>`.
|
|
1906
|
-
* 2. `navigator.language`.
|
|
1907
|
-
* 3. The hard-coded fallback `'en'`.
|
|
1908
|
-
*
|
|
1909
|
-
* The previous version of this library defaulted to `'de'` — a German
|
|
1910
|
-
* default that surprised consumers in other locales. We now use the
|
|
1911
|
-
* browser hint and fall back to English.
|
|
1912
|
-
*
|
|
1913
|
-
* @module @detection/locale
|
|
1914
|
-
*/
|
|
1915
|
-
/**
|
|
1916
|
-
* Returns a BCP-47 locale string suitable for `Intl.*` constructors.
|
|
1917
|
-
*
|
|
1918
|
-
* Always returns a non-empty string. When no signal is available the
|
|
1919
|
-
* fallback (`'en'`) is returned.
|
|
1920
|
-
*/
|
|
1367
|
+
/** The locale for `Intl` renders before Payload names one: `<html lang>`, then `navigator.language`, then `'en'`. */
|
|
1921
1368
|
declare function detectInitialLocale(): string;
|
|
1922
1369
|
|
|
1923
1370
|
/**
|
|
1924
|
-
*
|
|
1925
|
-
*
|
|
1926
|
-
*
|
|
1927
|
-
* the new value. The registry pattern decouples the lifecycle from the
|
|
1928
|
-
* concrete renderers, so:
|
|
1929
|
-
*
|
|
1930
|
-
* - Tests can substitute mock renderers.
|
|
1931
|
-
* - Plugins can register custom renderers via the high-level client.
|
|
1932
|
-
* - New field types ship without touching `lifecycle.ts`.
|
|
1933
|
-
*
|
|
1934
|
-
* The explicit aggregator in `@field-types/index` supplies the default
|
|
1935
|
-
* renderers as values, so it remains tree-shaking-safe without module import
|
|
1936
|
-
* side effects. This registry stores only explicit consumer overrides.
|
|
1937
|
-
*
|
|
1938
|
-
* @module @field-types/registry
|
|
1371
|
+
* Explicit renderer overrides, layered above the defaults each time a map is
|
|
1372
|
+
* built. The registry never retains the defaults, so stateful ones can be
|
|
1373
|
+
* recreated per client.
|
|
1939
1374
|
*/
|
|
1940
1375
|
|
|
1941
|
-
/**
|
|
1942
|
-
* Register or replace an explicit renderer override for future snapshots.
|
|
1943
|
-
*/
|
|
1376
|
+
/** Register or replace an override for maps built afterwards. */
|
|
1944
1377
|
declare function registerBuiltinRenderer(renderer: FieldRenderer): void;
|
|
1945
1378
|
|
|
1946
|
-
/**
|
|
1947
|
-
* Shared shapes for the field-type renderers.
|
|
1948
|
-
*
|
|
1949
|
-
* @module @field-types/types
|
|
1950
|
-
*/
|
|
1379
|
+
/** Populated value shapes the field renderers read. */
|
|
1951
1380
|
interface PayloadMedia {
|
|
1952
1381
|
readonly id?: string | number;
|
|
1953
1382
|
readonly url?: string;
|
|
@@ -1962,13 +1391,7 @@ interface PayloadMedia {
|
|
|
1962
1391
|
readonly height?: number;
|
|
1963
1392
|
}>>;
|
|
1964
1393
|
}
|
|
1965
|
-
/**
|
|
1966
|
-
* Generic relationship payload. The optional `TSlug` parameter is a
|
|
1967
|
-
* phantom marker that codegen uses to record the target collection(s)
|
|
1968
|
-
* without changing the runtime shape — `PayloadRelationship<'users'>`
|
|
1969
|
-
* and `PayloadRelationship<'users' | 'authors'>` behave identically at
|
|
1970
|
-
* runtime but carry different types at compile time.
|
|
1971
|
-
*/
|
|
1394
|
+
/** `TSlug` is a phantom marker codegen uses to record the target collection; the runtime shape is identical. */
|
|
1972
1395
|
interface PayloadRelationship<TSlug extends string = string> {
|
|
1973
1396
|
readonly id?: string | number;
|
|
1974
1397
|
readonly title?: string;
|
|
@@ -1979,145 +1402,137 @@ interface PayloadRelationship<TSlug extends string = string> {
|
|
|
1979
1402
|
readonly [extra: string]: unknown;
|
|
1980
1403
|
}
|
|
1981
1404
|
|
|
1982
|
-
/**
|
|
1983
|
-
* Public barrel for built-in field renderers.
|
|
1984
|
-
*
|
|
1985
|
-
* Each renderer is imported as a named binding (not a side-effect)
|
|
1986
|
-
* and assembled into the registry through `buildBuiltinRenderers`.
|
|
1987
|
-
* The bundler keeps every reference reachable, so the registry is
|
|
1988
|
-
* fully populated at runtime even under `sideEffects: false`.
|
|
1989
|
-
*
|
|
1990
|
-
* @module @field-types
|
|
1991
|
-
*/
|
|
1405
|
+
/** Built-in field renderers, assembled as values so `sideEffects: false` bundlers keep them all. */
|
|
1992
1406
|
|
|
1993
|
-
/**
|
|
1994
|
-
* Built-in renderer map. Called once per client/runtime; the
|
|
1995
|
-
* `structural-array` renderer gets a fresh instance with its own diff
|
|
1996
|
-
* state so nothing is shared between concurrent clients.
|
|
1997
|
-
*/
|
|
1407
|
+
/** One renderer map per client; the stateful text and structural renderers are created fresh each call. */
|
|
1998
1408
|
declare function buildBuiltinRenderers(): Readonly<Record<string, FieldRenderer>>;
|
|
1999
1409
|
|
|
2000
1410
|
/**
|
|
2001
|
-
* Protocol
|
|
2002
|
-
*
|
|
2003
|
-
*
|
|
2004
|
-
* postMessage protocol.** The admin never sends a `protocolVersion`
|
|
2005
|
-
* field and ignores extra fields in our `ready` handshake. Observed
|
|
2006
|
-
* wire behaviour:
|
|
2007
|
-
*
|
|
2008
|
-
* - Payload **2.x** sent `fieldSchemaJSON` with the first update
|
|
2009
|
-
* (client-side merge era).
|
|
2010
|
-
* - Payload **3.x** removed `fieldSchemaJSON` entirely; the official
|
|
2011
|
-
* client re-fetches merged documents through the REST API instead.
|
|
2012
|
-
* - No Payload version sends `previewToken` or any keepalive.
|
|
2013
|
-
*
|
|
2014
|
-
* This module therefore only matters for NON-stock admin panels — a
|
|
2015
|
-
* custom `window.postMessage` sender can advertise a version and this
|
|
2016
|
-
* library will negotiate `min(ours, theirs)` and expose capability
|
|
2017
|
-
* flags. Against stock Payload, `theirs` stays `undefined`, negotiation
|
|
2018
|
-
* collapses to v1, and nothing is gated on it: schema handling
|
|
2019
|
-
* activates whenever `fieldSchemaJSON` is actually present, and token
|
|
2020
|
-
* validation only runs when the consumer opts in.
|
|
2021
|
-
*
|
|
2022
|
-
* Version ladder (this library's own numbering, not Payload's):
|
|
2023
|
-
*
|
|
2024
|
-
* - **v1** — data + slug + locale (matches stock Payload of any era).
|
|
2025
|
-
* - **v2** — remote sends `fieldSchemaJSON` (Payload 2.x behaviour).
|
|
2026
|
-
* - **v3** — remote sends `previewToken` (custom senders only).
|
|
2027
|
-
* - **v4** — nested-array recursion / richer document events
|
|
2028
|
-
* (custom senders only).
|
|
2029
|
-
*
|
|
2030
|
-
* @module @core/protocol-version
|
|
1411
|
+
* Protocol negotiation. A capability is granted either by the version a peer
|
|
1412
|
+
* announces or by observation, since the stock Payload admin announces none:
|
|
1413
|
+
* what it can do is read off the messages it sends. See ADR 0010.
|
|
2031
1414
|
*/
|
|
1415
|
+
/** Bumped only when this library's understanding of the wire format changes. */
|
|
2032
1416
|
declare const LIBRARY_PROTOCOL_VERSION = 4;
|
|
2033
|
-
/**
|
|
2034
|
-
|
|
2035
|
-
|
|
1417
|
+
/** How a capability becomes active. */
|
|
1418
|
+
type CapabilitySource = 'version' | 'observed';
|
|
1419
|
+
interface CapabilityDeclaration {
|
|
1420
|
+
/** Enabled once the negotiated version reaches this (for announced peers). */
|
|
1421
|
+
readonly since: number;
|
|
1422
|
+
/** The wire signal that grants this capability to a peer announcing no version. */
|
|
1423
|
+
readonly observed?: 'schema' | 'locale' | 'preview-token' | 'document-event' | 'relationship-event';
|
|
1424
|
+
}
|
|
1425
|
+
/** What activates each capability; the prose lives in `CAPABILITY_DOCUMENTATION`. */
|
|
1426
|
+
declare const CAPABILITY_DECLARATIONS: {
|
|
1427
|
+
readonly basic: {
|
|
1428
|
+
readonly since: 1;
|
|
1429
|
+
};
|
|
1430
|
+
readonly 'schema-json': {
|
|
1431
|
+
readonly since: 2;
|
|
1432
|
+
readonly observed: "schema";
|
|
1433
|
+
};
|
|
1434
|
+
readonly locale: {
|
|
1435
|
+
readonly since: 2;
|
|
1436
|
+
readonly observed: "locale";
|
|
1437
|
+
};
|
|
1438
|
+
readonly 'preview-token': {
|
|
1439
|
+
readonly since: 3;
|
|
1440
|
+
readonly observed: "preview-token";
|
|
1441
|
+
};
|
|
1442
|
+
readonly 'nested-arrays': {
|
|
1443
|
+
readonly since: 4;
|
|
1444
|
+
};
|
|
1445
|
+
readonly 'recursive-diffs': {
|
|
1446
|
+
readonly since: 4;
|
|
1447
|
+
};
|
|
1448
|
+
readonly 'document-events': {
|
|
1449
|
+
readonly since: 4;
|
|
1450
|
+
readonly observed: "document-event";
|
|
1451
|
+
};
|
|
1452
|
+
readonly 'relationship-events': {
|
|
1453
|
+
readonly since: 4;
|
|
1454
|
+
readonly observed: "relationship-event";
|
|
1455
|
+
};
|
|
1456
|
+
};
|
|
1457
|
+
type ProtocolCapability = keyof typeof CAPABILITY_DECLARATIONS;
|
|
1458
|
+
/** Every declared capability, in declaration order. */
|
|
1459
|
+
declare const PROTOCOL_CAPABILITIES: readonly ProtocolCapability[];
|
|
2036
1460
|
interface ProtocolNegotiation {
|
|
2037
1461
|
/** This library's protocol version. */
|
|
2038
1462
|
readonly ours: number;
|
|
2039
|
-
/** The remote party's version, when
|
|
1463
|
+
/** The remote party's version, when it announced one. */
|
|
2040
1464
|
readonly theirs: number | undefined;
|
|
2041
1465
|
/** `min(ours, theirs ?? 1)` — the version both sides actually share. */
|
|
2042
1466
|
readonly negotiated: number;
|
|
2043
|
-
/**
|
|
2044
|
-
readonly capabilities: ReadonlySet<
|
|
1467
|
+
/** Capabilities active at the negotiated version or seen on the wire. */
|
|
1468
|
+
readonly capabilities: ReadonlySet<ProtocolCapability>;
|
|
1469
|
+
/** The subset that was observed on the wire rather than granted by version. */
|
|
1470
|
+
readonly observed: ReadonlySet<ProtocolCapability>;
|
|
2045
1471
|
}
|
|
2046
|
-
/**
|
|
2047
|
-
|
|
2048
|
-
*
|
|
2049
|
-
* `theirs === undefined` collapses to v1 — assume the remote party is
|
|
2050
|
-
* an older Payload that does not advertise a version.
|
|
2051
|
-
*
|
|
2052
|
-
* Non-finite or non-positive `theirs` values fall back to v1 as well —
|
|
2053
|
-
* we never trust an obviously-corrupted negotiation byte.
|
|
2054
|
-
*/
|
|
2055
|
-
declare function negotiateProtocol(theirs: number | undefined): ProtocolNegotiation;
|
|
1472
|
+
/** Deterministic; the runtime re-runs it whenever either input changes. */
|
|
1473
|
+
declare function negotiateProtocol(theirs: number | undefined, observed?: Iterable<ProtocolCapability>): ProtocolNegotiation;
|
|
2056
1474
|
declare function hasCapability(negotiation: ProtocolNegotiation, capability: ProtocolCapability): boolean;
|
|
2057
1475
|
|
|
2058
1476
|
/**
|
|
2059
|
-
*
|
|
2060
|
-
*
|
|
2061
|
-
*
|
|
2062
|
-
* The same path strings the runtime reads from `data-payload-field`
|
|
2063
|
-
* are typed here, so a typo like `'heroTitlee'` is rejected at compile
|
|
2064
|
-
* time *and* the runtime side does not need to change.
|
|
2065
|
-
*
|
|
2066
|
-
* @module @dsl/paths
|
|
1477
|
+
* What each capability tells the runtime, for `inspect()` and consumer
|
|
1478
|
+
* diagnostics. Kept out of the runtime module so the inline bundle ships the
|
|
1479
|
+
* rules and not this prose. See ADR 0010.
|
|
2067
1480
|
*/
|
|
1481
|
+
|
|
1482
|
+
interface CapabilityDocumentation {
|
|
1483
|
+
/** The runtime behaviour that depends on the capability. */
|
|
1484
|
+
readonly gates: string;
|
|
1485
|
+
/** What the runtime does without it. */
|
|
1486
|
+
readonly fallback: string;
|
|
1487
|
+
}
|
|
1488
|
+
declare const CAPABILITY_DOCUMENTATION: Readonly<Record<ProtocolCapability, CapabilityDocumentation>>;
|
|
1489
|
+
|
|
2068
1490
|
/**
|
|
2069
|
-
*
|
|
2070
|
-
*
|
|
1491
|
+
* What the peer does, derived from the capabilities its messages showed —
|
|
1492
|
+
* the runtime never asks which Payload version it is talking to. Unknown
|
|
1493
|
+
* until the admin reveals its shape. See ADR 0010.
|
|
2071
1494
|
*/
|
|
2072
|
-
|
|
1495
|
+
|
|
1496
|
+
type ProtocolProfileName = 'unknown' | 'payload-2' | 'payload-3';
|
|
1497
|
+
interface ProtocolProfile {
|
|
1498
|
+
readonly name: ProtocolProfileName;
|
|
1499
|
+
/** Where field types come from. */
|
|
1500
|
+
readonly fieldTyping: 'schema' | 'heuristic';
|
|
1501
|
+
/**
|
|
1502
|
+
* Who populates relationships. Payload 2.x's admin posts populated data
|
|
1503
|
+
* (it merges client-side with `fieldSchemaJSON`); Payload 3.x posts raw
|
|
1504
|
+
* form values and the runtime asks the REST API when `serverURL` is set.
|
|
1505
|
+
*/
|
|
1506
|
+
readonly populatesRelationships: 'admin' | 'server';
|
|
1507
|
+
/** Whether the peer reports document saves and relationship edits. */
|
|
1508
|
+
readonly documentEvents: boolean;
|
|
1509
|
+
}
|
|
2073
1510
|
/**
|
|
2074
|
-
*
|
|
2075
|
-
*
|
|
2076
|
-
*
|
|
2077
|
-
*
|
|
1511
|
+
* `schema-json` on the wire is the Payload 2.x signature: 3.x removed
|
|
1512
|
+
* `fieldSchemaJSON`. Document or relationship events are 3.x-only. Until
|
|
1513
|
+
* either shows, the peer is unknown and treated like 3.x for merging — the
|
|
1514
|
+
* conservative choice, because a needless REST merge costs a request while
|
|
1515
|
+
* a missing one loses populated relationships.
|
|
2078
1516
|
*/
|
|
1517
|
+
declare function detectProtocolProfile(observed: ReadonlySet<ProtocolCapability>): ProtocolProfile;
|
|
1518
|
+
|
|
1519
|
+
/** Type-level field paths, so a binding name is checked at compile time against the codegen schema. */
|
|
1520
|
+
/** String keys of `T`. */
|
|
1521
|
+
type FieldName<T> = Extract<keyof T, string>;
|
|
1522
|
+
/** Dotted paths into `T`, capped at three levels so IntelliSense stays usable. */
|
|
2079
1523
|
type FieldPath<T, Depth extends 0 | 1 | 2 | 3 = 3> = Depth extends 0 ? never : T extends readonly (infer U)[] ? FieldPath<U, Prev<Depth>> : T extends object ? {
|
|
2080
1524
|
[K in Extract<keyof T, string>]: K | (T[K] extends object ? `${K}.${FieldPath<T[K], Prev<Depth>>}` : never);
|
|
2081
1525
|
}[Extract<keyof T, string>] : never;
|
|
2082
|
-
/** Decrement helper for depth-bounded recursion. */
|
|
2083
1526
|
type Prev<N extends 0 | 1 | 2 | 3> = N extends 3 ? 2 : N extends 2 ? 1 : N extends 1 ? 0 : 0;
|
|
2084
|
-
/**
|
|
2085
|
-
* Resolve the value type at a dotted path. Returns `unknown` when the
|
|
2086
|
-
* path does not exist on `T`, so consumers can opt into stricter
|
|
2087
|
-
* `keyof`-based DSLs when they want absolute type safety.
|
|
2088
|
-
*/
|
|
1527
|
+
/** The value type at a dotted path, or `unknown` when the path does not exist. */
|
|
2089
1528
|
type ValueAt<T, P extends string> = P extends `${infer Head}.${infer Rest}` ? Head extends keyof T ? T[Head] extends readonly (infer U)[] ? ValueAt<U, Rest> : T[Head] extends object ? ValueAt<T[Head], Rest> : unknown : unknown : P extends keyof T ? T[P] : unknown;
|
|
2090
1529
|
|
|
2091
1530
|
/**
|
|
2092
|
-
* Typed
|
|
2093
|
-
*
|
|
2094
|
-
* of the field name.
|
|
2095
|
-
*
|
|
2096
|
-
* Two forms are exposed:
|
|
2097
|
-
*
|
|
2098
|
-
* - **`bind<Schema>('heroTitle')`** — string-literal form. Lightest
|
|
2099
|
-
* possible runtime (no Proxy), but a rename in the IDE does NOT
|
|
2100
|
-
* follow into the string.
|
|
2101
|
-
* - **`bindByPath<Schema>(s => s.heroTitle)`** — proxy form. The
|
|
2102
|
-
* callback walks a Proxy that records the access path, returning
|
|
2103
|
-
* the same shape as `bind` so the two are interchangeable. Slightly
|
|
2104
|
-
* heavier runtime, but rename-safe and discourages typos.
|
|
2105
|
-
*
|
|
2106
|
-
* Both helpers also accept a richer return — `bind('image', 'src')` —
|
|
2107
|
-
* to bind an attribute other than the default `data-payload-field`,
|
|
2108
|
-
* matching the cache resolver's `data-payload-attribute` flow.
|
|
2109
|
-
*
|
|
2110
|
-
* @module @dsl/bind
|
|
1531
|
+
* Typed helpers emitting the `data-payload-*` attributes the runtime reads.
|
|
1532
|
+
* `bindByPath` records the path through a Proxy, so a rename follows.
|
|
2111
1533
|
*/
|
|
2112
1534
|
|
|
2113
|
-
/**
|
|
2114
|
-
* Shape returned by every binding helper — a spreadable HTML attribute
|
|
2115
|
-
* record. Astro / JSX / Svelte all accept this style directly:
|
|
2116
|
-
*
|
|
2117
|
-
* ```astro
|
|
2118
|
-
* <h1 {...bind<Homepage>('heroTitle')}>{data.heroTitle}</h1>
|
|
2119
|
-
* ```
|
|
2120
|
-
*/
|
|
1535
|
+
/** A spreadable attribute record: `<h1 {...bind<Homepage>('heroTitle')}>`. */
|
|
2121
1536
|
interface FieldBindingAttributes {
|
|
2122
1537
|
readonly 'data-payload-field': string;
|
|
2123
1538
|
readonly 'data-payload-attribute'?: string;
|
|
@@ -2125,139 +1540,78 @@ interface FieldBindingAttributes {
|
|
|
2125
1540
|
readonly 'data-payload-richtext'?: string;
|
|
2126
1541
|
readonly 'data-payload-html'?: string;
|
|
2127
1542
|
readonly 'data-payload-locale'?: string;
|
|
1543
|
+
readonly 'data-payload-alt'?: string;
|
|
1544
|
+
readonly 'data-payload-href'?: string;
|
|
1545
|
+
readonly 'data-payload-array-template'?: string;
|
|
2128
1546
|
}
|
|
2129
1547
|
interface BindOptions {
|
|
2130
|
-
/**
|
|
1548
|
+
/** Attribute to write instead of the text content, e.g. `'src'` for `<img>`. */
|
|
2131
1549
|
readonly attribute?: string;
|
|
2132
|
-
/** Explicit field
|
|
1550
|
+
/** Explicit field type, bypassing schema detection. */
|
|
2133
1551
|
readonly type?: string;
|
|
2134
|
-
/**
|
|
2135
|
-
* Mark the binding as Lexical rich text. Usually detected automatically;
|
|
2136
|
-
* set it where the initial render is empty and there is nothing to detect.
|
|
2137
|
-
*/
|
|
1552
|
+
/** Mark the binding as Lexical rich text; needed only when the initial render is empty. */
|
|
2138
1553
|
readonly richtext?: boolean;
|
|
2139
1554
|
/** Render the value as sanitised HTML rather than text. */
|
|
2140
1555
|
readonly html?: boolean;
|
|
2141
1556
|
/** Lock this element to one locale, overriding the message locale. */
|
|
2142
1557
|
readonly locale?: string;
|
|
1558
|
+
/** Field whose value becomes the image `alt`. */
|
|
1559
|
+
readonly alt?: string;
|
|
1560
|
+
/** Field whose value becomes the link `href`. */
|
|
1561
|
+
readonly href?: string;
|
|
1562
|
+
/** Markup per array item with `{{value}}` placeholders. */
|
|
1563
|
+
readonly arrayTemplate?: string;
|
|
2143
1564
|
}
|
|
2144
|
-
/**
|
|
2145
|
-
* String-literal binding helper.
|
|
2146
|
-
*
|
|
2147
|
-
* ```ts
|
|
2148
|
-
* import { bind } from 'payload-live-preview';
|
|
2149
|
-
* import type { Homepage } from './payload-types';
|
|
2150
|
-
*
|
|
2151
|
-
* <h1 {...bind<Homepage>('heroTitle')}>{data.heroTitle}</h1>
|
|
2152
|
-
* <img {...bind<Homepage>('heroImage', { attribute: 'src' })} />
|
|
2153
|
-
* ```
|
|
2154
|
-
*
|
|
2155
|
-
* The generic `T` is the schema interface emitted by `pll-codegen`.
|
|
2156
|
-
* `field` is constrained to `keyof T` so misspellings fail at compile
|
|
2157
|
-
* time. When `T` is omitted, the helper accepts any string — useful
|
|
2158
|
-
* for incremental adoption.
|
|
2159
|
-
*/
|
|
1565
|
+
/** `bind<Homepage>('heroTitle')`; without `T` any string is accepted. */
|
|
2160
1566
|
declare function bind<T = Record<string, unknown>>(field: FieldName<T>, options?: BindOptions): FieldBindingAttributes;
|
|
2161
|
-
/**
|
|
2162
|
-
* Proxy-path binding helper.
|
|
2163
|
-
*
|
|
2164
|
-
* ```ts
|
|
2165
|
-
* <h1 {...bindByPath<Homepage>(d => d.heroTitle)}>{data.heroTitle}</h1>
|
|
2166
|
-
* ```
|
|
2167
|
-
*
|
|
2168
|
-
* The picker is invoked once with a recording Proxy; the recorded path
|
|
2169
|
-
* is joined with `.` and emitted as the binding name. Property access
|
|
2170
|
-
* on nested objects and arrays both work — `d => d.slides[0].title`
|
|
2171
|
-
* becomes `slides.title` (array indices are normalised away because the
|
|
2172
|
-
* runtime resolves bindings against schema, not against positional
|
|
2173
|
-
* indices).
|
|
2174
|
-
*/
|
|
1567
|
+
/** `bindByPath<Homepage>(d => d.slides[0].title)` → `slides.title`; array indices are dropped because bindings resolve against the schema. */
|
|
2175
1568
|
declare function bindByPath<T = Record<string, unknown>>(picker: (data: T) => unknown, options?: BindOptions): FieldBindingAttributes;
|
|
2176
1569
|
|
|
2177
1570
|
/**
|
|
2178
|
-
*
|
|
2179
|
-
*
|
|
2180
|
-
*
|
|
2181
|
-
* field, and `data-payload-owner` names a global, a collection and often a
|
|
2182
|
-
* document id. Emitted unconditionally they publish the shape of the content
|
|
2183
|
-
* model — and the identity of documents — to every anonymous visitor and
|
|
2184
|
-
* crawler of the page.
|
|
2185
|
-
*
|
|
2186
|
-
* The gate belongs to the application, not to this package: it is the same
|
|
2187
|
-
* decision that already controls draft reads and cache policy. What this
|
|
2188
|
-
* module provides is a place to apply that decision **once per request** so no
|
|
2189
|
-
* individual call site can forget it, and an emission unit that cannot be
|
|
2190
|
-
* partially suppressed.
|
|
2191
|
-
*
|
|
2192
|
-
* That second property matters more than it looks. A binding is not one
|
|
2193
|
-
* attribute: a field travels with its type, its locale, its rich-text marker
|
|
2194
|
-
* and its owner. Gating only the field name leaves the companions behind,
|
|
2195
|
-
* which discloses the taxonomy anyway and leaves the runtime looking at a
|
|
2196
|
-
* binding whose field is gone.
|
|
2197
|
-
*
|
|
2198
|
-
* @module @dsl/preview-bindings
|
|
1571
|
+
* Binding helpers gated on one authorization verdict per request: the
|
|
1572
|
+
* attributes publish the content model, so an unauthorized response carries
|
|
1573
|
+
* none and a binding is suppressed whole, never in part. See ADR 0006.
|
|
2199
1574
|
*/
|
|
2200
1575
|
|
|
2201
|
-
/** Owner marker naming the document a subtree belongs to. */
|
|
2202
1576
|
interface OwnerBindingAttributes {
|
|
2203
1577
|
readonly 'data-payload-owner': string;
|
|
2204
1578
|
}
|
|
2205
|
-
/**
|
|
2206
|
-
* The complete absence of binding attributes.
|
|
2207
|
-
*
|
|
2208
|
-
* Spreading this into markup contributes nothing, which is the point: an
|
|
2209
|
-
* unauthorized response must be byte-identical to one that never knew about
|
|
2210
|
-
* live preview.
|
|
2211
|
-
*/
|
|
1579
|
+
/** No attributes at all: an unauthorized response is byte-identical to one that never knew about live preview. */
|
|
2212
1580
|
type SuppressedBinding = Readonly<Record<string, never>>;
|
|
2213
|
-
interface
|
|
2214
|
-
/**
|
|
2215
|
-
* The application's own authorization verdict for this request.
|
|
2216
|
-
*
|
|
2217
|
-
* Never derive this from `?preview=true`, an iframe destination or a
|
|
2218
|
-
* referer — those are client-controlled intent signals. Use the same
|
|
2219
|
-
* verified result that decides whether drafts may be read.
|
|
2220
|
-
*/
|
|
2221
|
-
readonly authorized: boolean;
|
|
2222
|
-
/**
|
|
2223
|
-
* Document this subtree belongs to, emitted as `data-payload-owner` by
|
|
2224
|
-
* {@link PreviewBindings.owner}. Required only when the page previews more
|
|
2225
|
-
* than one document and the runtime runs with `scopeBindingsByOwner`.
|
|
2226
|
-
*/
|
|
1581
|
+
interface PreviewBindingsCommonOptions {
|
|
1582
|
+
/** Document this subtree belongs to, emitted as `data-payload-owner`; needed with `scopeBindingsByOwner`. */
|
|
2227
1583
|
readonly owner?: string;
|
|
2228
1584
|
}
|
|
2229
|
-
/**
|
|
1585
|
+
/** The verdict from `authorizePreviewRequest()`, or `null` for a public response. Only a branded context authorizes emission. */
|
|
1586
|
+
interface PreviewBindingsOptions extends PreviewBindingsCommonOptions {
|
|
1587
|
+
readonly authorization: AuthorizedPreviewContext | null;
|
|
1588
|
+
}
|
|
1589
|
+
/** Request-scoped binding helpers carrying one authorization decision. */
|
|
2230
1590
|
interface PreviewBindings {
|
|
2231
|
-
/** The verdict these helpers were built with. */
|
|
2232
1591
|
readonly authorized: boolean;
|
|
2233
|
-
/** Typed field binding, or nothing
|
|
1592
|
+
/** Typed field binding, or nothing while unauthorized. */
|
|
2234
1593
|
bind: <T = Record<string, unknown>>(field: FieldName<T>, options?: BindOptions) => FieldBindingAttributes | SuppressedBinding;
|
|
2235
|
-
/** Rename-safe field binding, or nothing
|
|
1594
|
+
/** Rename-safe field binding, or nothing while unauthorized. */
|
|
2236
1595
|
bindByPath: <T = Record<string, unknown>>(picker: (data: T) => unknown, options?: BindOptions) => FieldBindingAttributes | SuppressedBinding;
|
|
2237
|
-
/**
|
|
2238
|
-
* Owner marker for the element that owns this subtree, or nothing at all
|
|
2239
|
-
* while unauthorized or when no owner was configured.
|
|
2240
|
-
*/
|
|
1596
|
+
/** Owner marker for this subtree, or nothing while unauthorized or without an owner. */
|
|
2241
1597
|
owner: () => OwnerBindingAttributes | SuppressedBinding;
|
|
2242
1598
|
}
|
|
1599
|
+
/** Request-scoped `bind`, `bindByPath` and `owner`, all suppressed unless `authorization` is a real context. */
|
|
1600
|
+
declare function createPreviewBindings(options: PreviewBindingsOptions): PreviewBindings;
|
|
1601
|
+
|
|
2243
1602
|
/**
|
|
2244
|
-
*
|
|
2245
|
-
*
|
|
2246
|
-
*
|
|
2247
|
-
* const preview = createPreviewBindings({
|
|
2248
|
-
* authorized: authorization !== null,
|
|
2249
|
-
* owner: `global:${slug}`,
|
|
2250
|
-
* });
|
|
2251
|
-
* ```
|
|
2252
|
-
* ```astro
|
|
2253
|
-
* <section {...preview.owner()}>
|
|
2254
|
-
* <h1 {...preview.bind<Homepage>('heroTitle')}>{data.heroTitle}</h1>
|
|
2255
|
-
* </section>
|
|
2256
|
-
* ```
|
|
2257
|
-
*
|
|
2258
|
-
* While unauthorized every helper returns an empty attribute set, so the
|
|
2259
|
-
* public response carries no `data-payload-*` at all.
|
|
1603
|
+
* Hydrated islands own their subtree, so the runtime never patches inside one.
|
|
1604
|
+
* Instead every flush dispatches `payload-live-preview:update` on each island
|
|
1605
|
+
* root with the update in `detail`. `data-payload-island="patch"` opts back in.
|
|
2260
1606
|
*/
|
|
2261
|
-
declare
|
|
1607
|
+
declare const ISLAND_EVENT = "payload-live-preview:update";
|
|
1608
|
+
interface IslandUpdateDetail {
|
|
1609
|
+
readonly fields: Readonly<Record<string, unknown>>;
|
|
1610
|
+
readonly revision: number;
|
|
1611
|
+
readonly receivedAt: number;
|
|
1612
|
+
readonly locale: string | undefined;
|
|
1613
|
+
}
|
|
1614
|
+
/** Whether `element` (or an ancestor) is an island that did not opt into patching. */
|
|
1615
|
+
declare function isInsideIsland(element: Element): boolean;
|
|
2262
1616
|
|
|
2263
|
-
export { type AnalyticsSnapshot, type BindOptions, type BlockRenderContext, type BlockRenderer, type CachedElement, type ConnectionStatus, type CspDirectiveMerge, DIAGNOSTIC_CODES, type DiagnosticCode, type DocumentSaveHandler, type DocumentSavePluginOptions, type DocumentSaveStrategy, EventEmitter, type EventHandler, type
|
|
1617
|
+
export { AUTHORIZED_PREVIEW_BRAND_KEY, type AnalyticsSnapshot, type AuthorizedPreviewContext, type AuthorizedPreviewScope, type BindOptions, type BlockRenderContext, type BlockRenderer, CAPABILITY_DECLARATIONS, CAPABILITY_DOCUMENTATION, type CachedElement, type CapabilityDeclaration, type CapabilityDocumentation, type CapabilitySource, type ConnectionStatus, type CspDirectiveMerge, type CustomRendererKey, DIAGNOSTIC_CODES, type DefaultsProfile, type DiagnosticCode, type DocumentSaveHandler, type DocumentSavePluginOptions, type DocumentSaveStrategy, EventEmitter, type EventHandler, type EventSourcePolicy, type FetchLike, type FieldBindingAttributes, type FieldName, type FieldPath, type FieldRenderer, type FieldTransform, type FieldType, type FocusReportTarget, type FragmentContext, type FragmentReport, type FragmentStrategy, type FrameAncestorsOptions, ISLAND_EVENT, type InlineScriptConfig, type InspectionBindings, type InspectionFragments, type InspectionOrigins, type InspectionProtocol, type InspectionRevisions, type InspectionRoute, type InspectionScheduler, type IslandUpdateDetail, type IssuePreviewTokenOptions, LIBRARY_PROTOCOL_VERSION, type LexicalNode, type LexicalRoot, LivePreviewClient, type LivePreviewClientConfig, type LivePreviewEventMap, type LivePreviewInspection, type LivePreviewPlugin, type NavigationLifecycleOptions, type NavigationLifecycleTarget, OriginDetector, type OwnerBindingAttributes, PROTOCOL_CAPABILITIES, type PayloadDocumentEventDetail, type PayloadFieldCondition, type PayloadFieldSchema, type PayloadFieldType, type PayloadLivePreviewData, type PayloadLivePreviewMessage, type PayloadMedia, type PayloadRelationship, type PayloadSessionStrategy, type PluginCompatibility, type PluginContext, type PluginDisposer, type PluginEvents, type PluginInspection, type PreviewAuthorization, type PreviewAuthorizationOutcome, type PreviewAuthorizationRequest, type PreviewAuthorizationStrategy, type PreviewAuthorizationStrategyName, type PreviewBindings, type PreviewBindingsCommonOptions, type PreviewBindingsOptions, type PreviewFocusMessage, type PreviewRequestLike, type PreviewRequestOptions, type PreviewTokenClaims, type PreviewTokenReplayStore, type PreviewTokenTransport, type PreviewVerifierClaims, type ProtocolCapability, type ProtocolNegotiation, type ProtocolProfile, type ProtocolProfileName, type RenderContext, type RendererKey, type RichTextRenderer, type RouteContext, type RouteStrategy, type SanitizerDocument, type SanitizerPolicyMode, type SignedTokenStrategy, type StrategyHandlers, type SubtleCryptoLike, type SuppressedBinding, TRUSTED_TYPES_POLICY_NAME, type TrustedHtmlPolicyLike, type Unsubscribe, type UpdateSource, VERSION, type ValueAt, type VerifierStrategy, authorizePreviewRequest, bind, bindByPath, bindNavigationLifecycle, buildBuiltinRenderers, buildFrameAncestors, buildScriptSrcWithNonce, createAnalyticsPlugin, createPreviewBindings, createPreviewFocusReporter, debugPlugin, detectInitialLocale, detectProtocolProfile, documentSavePlugin, escapeHtml, escapeHtmlAttribute, generateCspNonce, generateInlineScript, hasCapability, hasPreviewIntent, highlightPlugin, initLivePreview, isAuthorizedPreviewContext, isDevMode, isExternalHttpUrl, isInIframe, isInPopup, isInPreviewContext, isInsideIsland, isLexicalContent, isSafeUrl, issuePreviewToken, lexicalToHtml, lexicalToPlainText, mergeCspHeader, negotiateProtocol, registerBlockRenderer, registerBuiltinRenderer, reportPreviewFocus, runtimeBuildInfo, sanitizeHtml, setCspCrypto, setSanitizerDocument, setSanitizerPolicy, setTrustedTypesPolicy, trustedHtml, wrapWithScriptTag };
|