payload-live-preview 1.0.2 → 1.0.4

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.
Files changed (71) hide show
  1. package/README.md +150 -84
  2. package/dist/adapters/astro/RichText.astro +8 -4
  3. package/dist/adapters/astro/index.d.ts +84 -9
  4. package/dist/adapters/astro/index.js +1 -363
  5. package/dist/adapters/astro/index.js.map +1 -1
  6. package/dist/adapters/astro/middleware-entry.d.ts +1 -1
  7. package/dist/adapters/astro/middleware-entry.js +1 -296
  8. package/dist/adapters/astro/middleware-entry.js.map +1 -1
  9. package/dist/adapters/nextjs/index.d.ts +24 -10
  10. package/dist/adapters/nextjs/index.js +1 -305
  11. package/dist/adapters/nextjs/index.js.map +1 -1
  12. package/dist/adapters/nuxt/index.d.ts +20 -10
  13. package/dist/adapters/nuxt/index.js +1 -293
  14. package/dist/adapters/nuxt/index.js.map +1 -1
  15. package/dist/adapters/sveltekit/index.d.ts +22 -6
  16. package/dist/adapters/sveltekit/index.js +1 -272
  17. package/dist/adapters/sveltekit/index.js.map +1 -1
  18. package/dist/codegen-astro.js +1 -609
  19. package/dist/codegen-astro.js.map +1 -1
  20. package/dist/codegen-cli.js +1 -665
  21. package/dist/codegen-cli.js.map +1 -1
  22. package/dist/codegen.cjs +1 -550
  23. package/dist/codegen.cjs.map +1 -1
  24. package/dist/codegen.js +1 -546
  25. package/dist/codegen.js.map +1 -1
  26. package/dist/core.cjs +1 -3719
  27. package/dist/core.cjs.map +1 -1
  28. package/dist/core.d.cts +1210 -7
  29. package/dist/core.d.ts +1210 -7
  30. package/dist/core.js +1 -3692
  31. package/dist/core.js.map +1 -1
  32. package/dist/index.cjs +1 -4118
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +1359 -34
  35. package/dist/index.d.ts +1359 -34
  36. package/dist/index.js +1 -4075
  37. package/dist/index.js.map +1 -1
  38. package/dist/{middleware-BwIlf0jW.d.cts → middleware-CkSeLZ-9.d.ts} +35 -19
  39. package/dist/payload.cjs +1 -30
  40. package/dist/payload.cjs.map +1 -1
  41. package/dist/payload.d.cts +4 -3
  42. package/dist/payload.d.ts +4 -3
  43. package/dist/payload.js +1 -28
  44. package/dist/payload.js.map +1 -1
  45. package/package.json +55 -16
  46. package/dist/adapters/astro/index.cjs +0 -370
  47. package/dist/adapters/astro/index.cjs.map +0 -1
  48. package/dist/adapters/astro/index.d.cts +0 -105
  49. package/dist/adapters/astro/middleware-entry.cjs +0 -303
  50. package/dist/adapters/astro/middleware-entry.cjs.map +0 -1
  51. package/dist/adapters/astro/middleware-entry.d.cts +0 -15
  52. package/dist/adapters/nextjs/index.cjs +0 -309
  53. package/dist/adapters/nextjs/index.cjs.map +0 -1
  54. package/dist/adapters/nextjs/index.d.cts +0 -75
  55. package/dist/adapters/nuxt/index.cjs +0 -299
  56. package/dist/adapters/nuxt/index.cjs.map +0 -1
  57. package/dist/adapters/nuxt/index.d.cts +0 -126
  58. package/dist/adapters/sveltekit/index.cjs +0 -275
  59. package/dist/adapters/sveltekit/index.cjs.map +0 -1
  60. package/dist/adapters/sveltekit/index.d.cts +0 -80
  61. package/dist/bind-BSgxoQhN.d.cts +0 -1137
  62. package/dist/bind-BSgxoQhN.d.ts +0 -1137
  63. package/dist/codegen-astro.cjs +0 -612
  64. package/dist/codegen-astro.cjs.map +0 -1
  65. package/dist/codegen-astro.d.cts +0 -54
  66. package/dist/codegen-cli.cjs +0 -669
  67. package/dist/codegen-cli.cjs.map +0 -1
  68. package/dist/codegen-cli.d.cts +0 -19
  69. package/dist/middleware-BwIlf0jW.d.ts +0 -168
  70. package/dist/preview-request-DAvxm18T.d.cts +0 -68
  71. package/dist/preview-request-DAvxm18T.d.ts +0 -68
package/dist/core.d.ts CHANGED
@@ -1,4 +1,1207 @@
1
- export { B as BindOptions, C as CachedElement, E as EventEmitter, b as EventHandler, F as FieldBindingAttributes, c as FieldName, d as FieldPath, e as FieldRenderer, g as FieldType, h as FrameAncestorsOptions, L as LIBRARY_PROTOCOL_VERSION, i as LivePreviewClient, j as LivePreviewClientConfig, k as LivePreviewEventMap, O as OriginDetector, P as PayloadFieldSchema, m as PayloadFieldType, n as PayloadLivePreviewData, o as PayloadLivePreviewMessage, q as ProtocolCapability, r as ProtocolNegotiation, R as RenderContext, S as SanitizerDocument, U as Unsubscribe, V as VERSION, s as ValueAt, t as bind, u as bindByPath, v as buildFrameAncestors, w as buildScriptSrcWithNonce, x as detectInitialLocale, y as escapeHtml, z as escapeHtmlAttribute, A as generateCspNonce, D as hasCapability, G as initLivePreview, H as isDevMode, I as isExternalHttpUrl, J as isInIframe, K as isInPopup, M as isInPreviewContext, N as isSafeUrl, T as negotiateProtocol, W as sanitizeHtml, X as setCspCrypto, Y as setSanitizerDocument } from './bind-BSgxoQhN.js';
1
+ /**
2
+ * Payload Live Preview message protocol.
3
+ *
4
+ * Mirrors the messages emitted by Payload CMS through
5
+ * `window.postMessage`, verified against the shipped admin code
6
+ * (`@payloadcms/ui` → `elements/LivePreview/Window`):
7
+ *
8
+ * - Payload **3.x** sends `{ type, collectionSlug, globalSlug, data,
9
+ * locale, externallyUpdatedRelationship }` on every form edit —
10
+ * `data` holds **raw form values** (relationships as bare IDs) and
11
+ * there is **no** `fieldSchemaJSON`.
12
+ * - Payload **2.x** additionally sent `fieldSchemaJSON` with the
13
+ * first update (client-side merge era).
14
+ * - `previewToken` and `protocolVersion` are extensions of THIS
15
+ * library for custom admin senders; stock Payload never sends
16
+ * them and ignores them in our `ready` handshake.
17
+ *
18
+ * @module @/types/payload-protocol
19
+ */
20
+ /**
21
+ * A `payload-live-preview` data update.
22
+ *
23
+ * `data` is the serialized document state. `fieldSchemaJSON`, when
24
+ * present (Payload 2.x or custom senders), allows the schema-driven
25
+ * engine to resolve field types without any DOM annotations.
26
+ */
27
+ interface PayloadLivePreviewMessage {
28
+ readonly type: 'payload-live-preview';
29
+ readonly data?: Record<string, unknown>;
30
+ readonly fieldSchemaJSON?: readonly PayloadFieldSchema[];
31
+ readonly globalSlug?: string;
32
+ readonly collectionSlug?: string;
33
+ readonly locale?: string;
34
+ readonly ready?: boolean;
35
+ /**
36
+ * Payload 3.x: most recent relationship-document event (create/update
37
+ * in a drawer), or `null`. The official client ignores it too — it is
38
+ * modeled here for completeness and consumer event access.
39
+ */
40
+ readonly externallyUpdatedRelationship?: PayloadDocumentEventDetail | null;
41
+ /**
42
+ * Optional preview JWT. ⚠️ Library extension — stock Payload never
43
+ * sends one. When `RuntimeOptions.validateToken` is set, the runtime
44
+ * requires every data update to carry a token that passes the
45
+ * validator; messages without a valid token are dropped.
46
+ */
47
+ readonly previewToken?: string;
48
+ /**
49
+ * Highest protocol version the sender supports. ⚠️ Library extension
50
+ * for custom senders — stock Payload sends no version. When absent,
51
+ * the receiver assumes v1.
52
+ */
53
+ readonly protocolVersion?: number;
54
+ }
55
+ /**
56
+ * Payload's `DocumentEvent` shape (admin `providers/DocumentEvents`).
57
+ */
58
+ interface PayloadDocumentEventDetail {
59
+ readonly entitySlug: string;
60
+ readonly operation?: 'create' | 'update';
61
+ readonly id?: string | number;
62
+ readonly updatedAt?: string;
63
+ readonly [extra: string]: unknown;
64
+ }
65
+ /**
66
+ * Parsed, validated message payload exposed to consumers via events.
67
+ */
68
+ interface PayloadLivePreviewData {
69
+ readonly fields: Record<string, unknown>;
70
+ readonly schema?: readonly PayloadFieldSchema[];
71
+ readonly globalSlug?: string;
72
+ readonly collectionSlug?: string;
73
+ readonly locale?: string;
74
+ }
75
+ /**
76
+ * The subset of Payload's field-schema description we rely on.
77
+ *
78
+ * Payload emits the entire field configuration tree but we only need
79
+ * the discriminator `type` and the nesting via `fields`/`blocks`.
80
+ * Extra properties are preserved opaquely.
81
+ */
82
+ interface PayloadFieldSchema {
83
+ readonly name: string;
84
+ readonly type: PayloadFieldType;
85
+ readonly label?: string;
86
+ readonly required?: boolean;
87
+ readonly localized?: boolean;
88
+ readonly fields?: readonly PayloadFieldSchema[];
89
+ readonly blocks?: readonly PayloadBlockSchema[];
90
+ readonly relationTo?: string | readonly string[];
91
+ readonly hasMany?: boolean;
92
+ readonly admin?: {
93
+ readonly condition?: PayloadFieldCondition;
94
+ };
95
+ readonly [extra: string]: unknown;
96
+ }
97
+ interface PayloadBlockSchema {
98
+ readonly slug: string;
99
+ readonly fields: readonly PayloadFieldSchema[];
100
+ readonly [extra: string]: unknown;
101
+ }
102
+ /**
103
+ * The Payload core field types this library recognises (20 as of
104
+ * Payload 3.x). `tabs` is treated as a structural container; `group`
105
+ * flattens nested fields.
106
+ */
107
+ type PayloadFieldType = 'text' | 'textarea' | 'richText' | 'email' | 'number' | 'checkbox' | 'date' | 'select' | 'radio' | 'array' | 'blocks' | 'group' | 'tabs' | 'row' | 'collapsible' | 'relationship' | 'upload' | 'point' | 'json' | 'code' | 'ui';
108
+ /**
109
+ * Payload's `admin.condition` is a function in source, but it cannot
110
+ * be transferred over postMessage. The schema arrives with this slot
111
+ * either absent or, in future Payload versions, replaced by a
112
+ * serialized expression. We model the slot but do not consume it yet.
113
+ */
114
+ type PayloadFieldCondition = (data: unknown, siblingData: unknown) => boolean;
115
+
116
+ /**
117
+ * Public event payload shapes for the live preview lifecycle.
118
+ *
119
+ * The map keys form the union of valid event names. Adding a new event
120
+ * is a two-line change: add the entry here and dispatch from the
121
+ * relevant module. Consumers get autocompletion and exhaustive checks
122
+ * for free because the `EventEmitter` is generic over this map.
123
+ *
124
+ * @module @events/types
125
+ */
126
+
127
+ interface LivePreviewEventMap {
128
+ /**
129
+ * Fired once per startup attempt after observers, cache, and message listening
130
+ * are active. A later startup failure may roll that attempt back; retrying then
131
+ * emits a new `init` event.
132
+ */
133
+ readonly init: {
134
+ readonly timestamp: number;
135
+ };
136
+ /** Fired for the first accepted data-bearing update from a trusted origin. */
137
+ readonly connect: {
138
+ readonly origin: string;
139
+ readonly timestamp: number;
140
+ };
141
+ /**
142
+ * Fired when the heartbeat times out or the runtime is destroyed. The
143
+ * `'unload'` reason remains in the public union for 1.x producer compatibility;
144
+ * the built-in runtime does not currently emit it.
145
+ */
146
+ readonly disconnect: {
147
+ readonly reason: 'timeout' | 'destroy' | 'unload';
148
+ readonly timestamp: number;
149
+ };
150
+ /**
151
+ * Fired before the DOM is mutated for an incoming update.
152
+ *
153
+ * Handlers may call `cancel()` before their synchronous or asynchronous
154
+ * work settles to skip exactly this revision.
155
+ */
156
+ readonly beforeUpdate: {
157
+ readonly data: PayloadLivePreviewData;
158
+ /** Runtime-populated ordering metadata; optional for 1.x producer compatibility. */
159
+ readonly revision?: number;
160
+ readonly cancel: () => void;
161
+ };
162
+ /**
163
+ * Fired after each actual, current DOM application batch. Visibility replay
164
+ * may produce another event for the same revision; cancelled, obsolete, and
165
+ * deferred-only batches produce none.
166
+ */
167
+ readonly afterUpdate: {
168
+ readonly data: PayloadLivePreviewData;
169
+ readonly updatedCount: number;
170
+ readonly durationMs: number;
171
+ /** Runtime-populated ordering metadata; optional for 1.x producer compatibility. */
172
+ readonly revision?: number;
173
+ };
174
+ /** Fired for each successful, still-current element write. */
175
+ readonly elementUpdate: {
176
+ readonly element: Element;
177
+ readonly fieldName: string;
178
+ readonly previousValue: unknown;
179
+ readonly nextValue: unknown;
180
+ /** Runtime-populated ordering metadata; optional for 1.x producer compatibility. */
181
+ readonly revision?: number;
182
+ };
183
+ /** Fired whenever the element cache is rebuilt (initial scan or MutationObserver-triggered). */
184
+ readonly cacheRefresh: {
185
+ readonly elementCount: number;
186
+ readonly fieldCount: number;
187
+ readonly durationMs: number;
188
+ };
189
+ /** Fired when a `payload-document-event` message arrives (document save). */
190
+ readonly documentSave: {
191
+ readonly timestamp: number;
192
+ };
193
+ /**
194
+ * Fired on errors that the runtime caught but cannot fully recover from.
195
+ *
196
+ * The `context` string identifies where the error originated.
197
+ */
198
+ readonly error: {
199
+ readonly error: Error;
200
+ readonly context: string;
201
+ };
202
+ /**
203
+ * Fired during destroy after browser observers and the message listener are
204
+ * removed. Event/plugin listeners remain available for this notification and
205
+ * are released by the owning client afterwards.
206
+ */
207
+ readonly destroy: {
208
+ readonly timestamp: number;
209
+ };
210
+ }
211
+ /**
212
+ * Handler signature: receives the payload and may return a Promise.
213
+ *
214
+ * Promise-returning handlers are awaited in registration order — this
215
+ * is important for `beforeUpdate.cancel()` semantics.
216
+ */
217
+ type EventHandler<TPayload> = (payload: TPayload) => void | Promise<void>;
218
+ /** Subscription removal function returned by `on()`/`once()`. */
219
+ type Unsubscribe = () => void;
220
+
221
+ /**
222
+ * Per-instance, type-safe event emitter.
223
+ *
224
+ * Replaces the legacy module-level singleton. Each `LivePreviewClient`
225
+ * instantiates its own emitter so that calling `destroy()` on one
226
+ * instance cannot clobber listeners attached to another.
227
+ *
228
+ * Design notes:
229
+ *
230
+ * - Generic over an event map type. The default `LivePreviewEventMap`
231
+ * captures every event this library emits, but the implementation
232
+ * is reusable for any map.
233
+ * - Handlers may be `async`. They are awaited sequentially in
234
+ * registration order. This is intentional: `beforeUpdate.cancel()`
235
+ * and similar synchronization rely on stable ordering.
236
+ * - Errors thrown by one handler are logged via `console.error` and
237
+ * do not prevent later handlers from running.
238
+ * - `once()` semantics: registered into a separate map; the entire
239
+ * snapshot for an event is claimed immediately before its dispatch,
240
+ * and remains consumed even if individual handlers throw.
241
+ *
242
+ * @module @events/emitter
243
+ */
244
+
245
+ declare class EventEmitter<TMap extends object = LivePreviewEventMap> {
246
+ #private;
247
+ /**
248
+ * Subscribe `handler` to `event`. Returns an unsubscribe function;
249
+ * call it to remove the handler. The handler is invoked for every
250
+ * subsequent emit until unsubscribed.
251
+ */
252
+ on<E extends keyof TMap>(event: E, handler: EventHandler<TMap[E]>): Unsubscribe;
253
+ /**
254
+ * Subscribe `handler` for a single emit. After the first dispatch,
255
+ * the handler is removed automatically.
256
+ */
257
+ once<E extends keyof TMap>(event: E, handler: EventHandler<TMap[E]>): Unsubscribe;
258
+ /**
259
+ * Remove `handler` from `event`. Works for both `on`- and
260
+ * `once`-registered handlers. No-op when the handler isn't registered.
261
+ */
262
+ off<E extends keyof TMap>(event: E, handler: EventHandler<TMap[E]>): void;
263
+ /**
264
+ * Dispatch `payload` to every handler registered for `event`.
265
+ *
266
+ * Handlers run sequentially in registration order — regular ones
267
+ * first, then `once` handlers. Async handlers are awaited. Synchronous
268
+ * exceptions are caught, logged, and isolated to the offending handler.
269
+ */
270
+ emit<E extends keyof TMap>(event: E, payload: TMap[E]): Promise<void>;
271
+ /**
272
+ * Lifecycle-only guarded dispatch. The predicate is checked before and after
273
+ * every handler so an async boundary cannot let an obsolete transaction run
274
+ * later callbacks. `emit()` intentionally keeps its established behaviour.
275
+ *
276
+ * Once-handlers are claimed only when dispatch reaches the once phase. Once
277
+ * claimed, the whole snapshot remains consumed even if the predicate changes
278
+ * while one of its handlers is awaiting, matching normal once semantics.
279
+ *
280
+ * @internal
281
+ * @returns `true` when the complete handler sequence ran while eligible.
282
+ */
283
+ emitWhile<E extends keyof TMap>(event: E, payload: TMap[E], shouldContinue: () => boolean): Promise<boolean>;
284
+ /**
285
+ * Number of handlers currently registered for `event`.
286
+ * Sum of `on` and `once` registrations.
287
+ */
288
+ listenerCount(event: keyof TMap): number;
289
+ /**
290
+ * Remove every handler. Pass an event name to limit the removal to
291
+ * one event; omit it to clear every event on this emitter.
292
+ *
293
+ * Crucially this only affects the current instance — there is no
294
+ * shared global state.
295
+ */
296
+ removeAllListeners(event?: keyof TMap): void;
297
+ /**
298
+ * Snapshot of all event names that currently have at least one
299
+ * registered handler. Useful for debugging.
300
+ */
301
+ eventNames(): (keyof TMap)[];
302
+ }
303
+
304
+ /**
305
+ * Core type definitions shared across all runtime primitives.
306
+ *
307
+ * @module @core/types
308
+ */
309
+
310
+ /**
311
+ * Field types recognized by the live preview renderer.
312
+ *
313
+ * Extends Payload's field set with `html` and `url` which are not
314
+ * Payload field types per se but useful DOM-binding categories.
315
+ */
316
+ type FieldType = PayloadFieldType | 'html' | 'url' | 'image' | 'structural-array';
317
+ /**
318
+ * A DOM element that has been registered as a live preview binding.
319
+ *
320
+ * Stored in the element cache indexed by `fieldName`. Multiple cached
321
+ * entries can share a field name when the same field is rendered in
322
+ * several places (e.g., a title shown in both the header and the
323
+ * page body).
324
+ */
325
+ interface CachedElement {
326
+ /** The bound DOM element. */
327
+ readonly element: Element;
328
+ /** Payload field path, e.g., `title` or `hero.subtitle`. */
329
+ readonly fieldName: string;
330
+ /**
331
+ * Resolved field type, used to dispatch to the right renderer.
332
+ *
333
+ * The cache resolves this from DOM attributes (`data-payload-type`,
334
+ * tag-based heuristics) at build time. When `explicitFieldType` is
335
+ * `false` the lifecycle is allowed to override this with the type
336
+ * learned from the schema once `fieldSchemaJSON` arrives.
337
+ */
338
+ readonly fieldType: FieldType;
339
+ /**
340
+ * `true` when the field type was set by an explicit
341
+ * `data-payload-type` attribute (consumer trumps schema), `false`
342
+ * when it was inferred via tag-based heuristics (schema may
343
+ * override later). Optional so synthetic bindings (tests / programmatic
344
+ * `cache.add()` callers) need not supply it; the cache always
345
+ * populates it for DOM-derived bindings.
346
+ */
347
+ readonly explicitFieldType?: boolean;
348
+ /**
349
+ * Optional target attribute: the value is written to this attribute
350
+ * instead of the element's content (from `data-payload-attribute`,
351
+ * or `bind(field, { attribute })`). Writes are policed — event
352
+ * handlers, `style`, and unsafe URLs are refused.
353
+ */
354
+ readonly targetAttribute?: string;
355
+ /** Optional sibling-field path bound to the element's `href` attribute. */
356
+ readonly hrefField?: string;
357
+ /** Optional sibling-field path bound to the element's `src` attribute. */
358
+ readonly srcField?: string;
359
+ /** Optional sibling-field path bound to the element's `alt` attribute. */
360
+ readonly altField?: string;
361
+ /** Optional inline template for array/blocks rendering. */
362
+ readonly arrayTemplate?: string;
363
+ /** Optional separator for primitive-array stringification. */
364
+ readonly arraySeparator?: string;
365
+ /** Optional locale code locked onto this element (overrides the global locale). */
366
+ readonly locale?: string;
367
+ }
368
+ /**
369
+ * Context passed to every field renderer. Provides access to the
370
+ * entire field tree so renderers can resolve sibling fields (e.g.,
371
+ * an `<a>` that pulls `href` from a different field).
372
+ */
373
+ interface RenderContext {
374
+ /** Full field map for the current update — used for sibling lookups. */
375
+ readonly allFields: Record<string, unknown>;
376
+ /** Active locale, or `undefined` when none is established. */
377
+ readonly locale: string | undefined;
378
+ /** Optional schema descriptor for the field, when available. */
379
+ readonly schema: PayloadFieldSchema | undefined;
380
+ }
381
+ /**
382
+ * Contract that every field renderer implements.
383
+ *
384
+ * Renderers are pure DOM-write functions: they receive the cached
385
+ * binding plus the new value and apply it to the element. They must
386
+ * not throw — failures should be silenced and logged so that one bad
387
+ * field cannot stop an entire update. The public renderer result remains
388
+ * `void` for 1.x compatibility. Built-in renderers may use an internal exact
389
+ * `false` sentinel when they deliberately perform no DOM write; callers must
390
+ * not rely on that implementation detail.
391
+ */
392
+ interface FieldRenderer {
393
+ readonly name: FieldType;
394
+ render(target: CachedElement, value: unknown, context: RenderContext): void;
395
+ }
396
+
397
+ /**
398
+ * Plugin system types.
399
+ *
400
+ * Each plugin participates in the live-preview lifecycle through a
401
+ * scoped, per-instance context. Plugins can register custom field
402
+ * renderers, transform incoming values, and subscribe to events —
403
+ * the same primitives the built-in renderers use.
404
+ *
405
+ * The system is intentionally minimal. Anything that can be expressed
406
+ * with an event subscription belongs in `events`, not `plugins`.
407
+ *
408
+ * @module @plugins/types
409
+ */
410
+
411
+ /** Idempotent handle that releases one plugin-owned resource. */
412
+ type PluginDisposer = () => void;
413
+ /**
414
+ * Event subscriptions scoped to one plugin registration.
415
+ *
416
+ * This remains nominally compatible with the pre-1.0.4 `EventEmitter` context
417
+ * type. Its methods are registration-scoped: even bulk removal affects only
418
+ * this plugin's subscriptions, never a consumer's or another plugin's
419
+ * listeners.
420
+ */
421
+ interface PluginEvents extends EventEmitter {
422
+ readonly on: <E extends keyof LivePreviewEventMap>(event: E, handler: EventHandler<LivePreviewEventMap[E]>) => Unsubscribe;
423
+ readonly once: <E extends keyof LivePreviewEventMap>(event: E, handler: EventHandler<LivePreviewEventMap[E]>) => Unsubscribe;
424
+ readonly off: <E extends keyof LivePreviewEventMap>(event: E, handler: EventHandler<LivePreviewEventMap[E]>) => void;
425
+ /** Emit through the owning client's event channel. */
426
+ readonly emit: <E extends keyof LivePreviewEventMap>(event: E, payload: LivePreviewEventMap[E]) => Promise<void>;
427
+ /**
428
+ * Guarded emit through the owning client channel. Dispatch stops when the
429
+ * caller predicate becomes false or this plugin registration is removed.
430
+ */
431
+ readonly emitWhile: <E extends keyof LivePreviewEventMap>(event: E, payload: LivePreviewEventMap[E], shouldContinue: () => boolean) => Promise<boolean>;
432
+ /** Number of listeners owned by this plugin registration. */
433
+ readonly listenerCount: (event: keyof LivePreviewEventMap) => number;
434
+ /** Remove only listeners owned by this plugin registration. */
435
+ readonly removeAllListeners: (event?: keyof LivePreviewEventMap) => void;
436
+ /** Event names for which this plugin registration owns listeners. */
437
+ readonly eventNames: () => (keyof LivePreviewEventMap)[];
438
+ }
439
+ /**
440
+ * Synchronous function that transforms a merged field value while a revision's
441
+ * per-binding scheduler entry is prepared. Transforms run in registration
442
+ * order and each receives the preceding transform's result. The final value is
443
+ * frozen into that entry before later renderer or attribute dispatch;
444
+ * `allFields` remains the merged, untransformed update snapshot.
445
+ *
446
+ * Returning a different shape than the input is allowed, but returning a
447
+ * Promise or other thenable is a contract error. If a transform throws or
448
+ * returns a thenable, the chain stops, the runtime emits an `error` event, and
449
+ * dispatch falls back to the original merged value. That fallback still passes
450
+ * through the normal renderer and attribute security controls.
451
+ */
452
+ type FieldTransform = (value: unknown, context: {
453
+ readonly fieldName: string;
454
+ readonly element: Element;
455
+ readonly allFields: Record<string, unknown>;
456
+ }) => unknown;
457
+ /**
458
+ * Per-instance plugin context. Replaces the legacy singleton context.
459
+ *
460
+ * Plugins receive a context that is bound to the parent
461
+ * `LivePreviewClient`. They can:
462
+ *
463
+ * - subscribe via `events.on/once`,
464
+ * - register field renderers,
465
+ * - register value transforms,
466
+ * - read the client's (frozen) configuration,
467
+ * - log through the client's debug channel.
468
+ */
469
+ interface PluginContext {
470
+ readonly events: PluginEvents;
471
+ /** Register a renderer owned by this plugin registration. */
472
+ readonly registerFieldRenderer: (renderer: FieldRenderer) => void;
473
+ /** Register an ordered transform owned by this plugin registration. */
474
+ readonly registerTransform: (fieldName: string, transform: FieldTransform) => void;
475
+ /**
476
+ * Register any additional synchronous cleanup owned by this registration.
477
+ * The manager invokes it during rollback, `unuse()`, or client destruction.
478
+ * Optional so pre-1.0.4 structural context mocks remain assignable.
479
+ */
480
+ readonly registerCleanup?: (cleanup: PluginDisposer) => void;
481
+ readonly getConfig: () => Readonly<Record<string, unknown>>;
482
+ readonly log: (...args: unknown[]) => void;
483
+ }
484
+ /**
485
+ * Plugin definition. `init` runs once per registration; `destroy`
486
+ * runs when the plugin is unregistered or the client is destroyed.
487
+ *
488
+ * Both hooks may return a promise; the manager awaits them.
489
+ */
490
+ interface LivePreviewPlugin {
491
+ readonly name: string;
492
+ readonly version?: string;
493
+ readonly init: (context: PluginContext) => void | Promise<void>;
494
+ readonly destroy?: () => void | Promise<void>;
495
+ }
496
+
497
+ /**
498
+ * `LivePreviewClient` configuration shape.
499
+ *
500
+ * @module @client/config
501
+ */
502
+ interface LivePreviewClientConfig {
503
+ /** Explicit additional trusted origins. */
504
+ readonly allowedOrigins?: readonly string[];
505
+ /**
506
+ * Payload server origin, e.g. `https://cms.example.com`. When set,
507
+ * every incoming update is re-fetched through the Payload REST API
508
+ * so relationship/upload fields render populated instead of as bare
509
+ * IDs (same strategy as the official client). Recommended for
510
+ * Payload 3.x.
511
+ */
512
+ readonly serverURL?: string;
513
+ /** REST API route prefix used with `serverURL`. Defaults to `/api`. */
514
+ readonly apiRoute?: string;
515
+ /** Population depth used with `serverURL`. Defaults to `1`. */
516
+ readonly mergeDepth?: number;
517
+ /**
518
+ * Custom fetch implementation for the `serverURL` merge request —
519
+ * the equivalent of the official client's `requestHandler`. Use it
520
+ * to attach auth headers or route through your own proxy. Only
521
+ * available on the programmatic client (functions cannot be
522
+ * serialised into the inline script).
523
+ */
524
+ readonly mergeFetch?: typeof fetch;
525
+ /** Enable verbose console debug output. Defaults to dev-mode detection. */
526
+ readonly debug?: boolean;
527
+ /** Debounce window for incoming updates. Defaults to 50 ms. */
528
+ readonly debounceMs?: number;
529
+ /**
530
+ * Heartbeat timeout in ms. Defaults to `0` (disabled) because the
531
+ * Payload admin sends messages only on form edits — there is no
532
+ * protocol keepalive, so an idle timeout would produce false
533
+ * disconnects.
534
+ */
535
+ readonly heartbeatMs?: number;
536
+ /** IntersectionObserver rootMargin. Defaults to `200px`. */
537
+ readonly intersectionRootMargin?: string;
538
+ /** Bypass the visibility gate (apply every update). Defaults to `false`. */
539
+ readonly disableVisibilityGate?: boolean;
540
+ /** Cache-size threshold above which off-screen updates are queued for replay. Defaults to 50. */
541
+ readonly visibilityGateThreshold?: number;
542
+ /**
543
+ * Mount an `aria-live` region for connections, applied updates, and mounted
544
+ * heartbeat-timeout disconnects. Destroy releases it synchronously. Default `true`.
545
+ */
546
+ readonly enableA11y?: boolean;
547
+ /** Locale used to pick A11y announcement strings. Defaults to detected locale. */
548
+ readonly a11yLocale?: string;
549
+ /** Document root, defaults to `document`. */
550
+ readonly root?: Document | Element;
551
+ /** Disable `document.referrer` detection. */
552
+ readonly disableReferrerDetection?: boolean;
553
+ /** Disable the localhost dev-mode pattern matcher. */
554
+ readonly disableLocalhostMatching?: boolean;
555
+ /** Auto-start the runtime in the constructor. Defaults to `true`. */
556
+ readonly autoStart?: boolean;
557
+ /**
558
+ * Optional preview-token validator. When set, every data update
559
+ * must carry a `previewToken` field that this function approves.
560
+ *
561
+ * ⚠️ `previewToken` is an extension of THIS library, not part of the
562
+ * stock Payload postMessage protocol — the Payload admin never sends
563
+ * one. Only enable this when a custom admin component adds the token
564
+ * to outgoing messages; against an unmodified Payload admin it would
565
+ * drop every update.
566
+ */
567
+ readonly validateToken?: (token: string | undefined, origin: string) => boolean | Promise<boolean>;
568
+ }
569
+
570
+ /**
571
+ * Public, high-level `LivePreviewClient`.
572
+ *
573
+ * The client wraps a `LivePreviewRuntime` with:
574
+ *
575
+ * - Per-instance event emitter (no singleton).
576
+ * - Per-instance plugin manager.
577
+ * - Custom-renderer registration.
578
+ * - Field-name transforms frozen into revision-bound scheduler entries before
579
+ * their later attribute or renderer dispatch.
580
+ *
581
+ * Consumers who want full programmatic control instantiate
582
+ * `LivePreviewClient` directly. Consumers who just want a working
583
+ * inline preview should use `generateInlineScript()` instead.
584
+ *
585
+ * @module @client
586
+ */
587
+
588
+ declare class LivePreviewClient {
589
+ #private;
590
+ constructor(config?: LivePreviewClientConfig);
591
+ /**
592
+ * Start the runtime. Returns `true` when this client is eligible and runtime
593
+ * startup is active or scheduled for DOM readiness. Repeated calls on an
594
+ * active client also return `true`; `false` means the client was destroyed or
595
+ * the page is outside a preview context. A deferred startup can still fail,
596
+ * roll back, and be retried by a later call.
597
+ */
598
+ start(): boolean;
599
+ /**
600
+ * Stop the runtime and tear down every plugin. Idempotent.
601
+ */
602
+ destroy(): Promise<void>;
603
+ /**
604
+ * Register a plugin.
605
+ */
606
+ use(plugin: LivePreviewPlugin): Promise<void>;
607
+ /** Unregister a plugin and release all resources owned by that registration. */
608
+ unuse(name: string): Promise<void>;
609
+ /** Rebuild the element cache manually. */
610
+ refreshCache(): void;
611
+ /** Read-only access to the event emitter for `on`/`once`/`off`. */
612
+ get events(): EventEmitter;
613
+ /** Names of currently registered plugins. */
614
+ get plugins(): readonly string[];
615
+ /** Current connection status. */
616
+ get status(): 'disconnected' | 'connecting' | 'connected';
617
+ /** Number of valid updates received so far. */
618
+ get updateCount(): number;
619
+ /** `true` once `destroy()` has been called. */
620
+ get destroyed(): boolean;
621
+ }
622
+ /**
623
+ * Convenience factory: instantiate the client, returning `null` when
624
+ * the page is not currently a preview context (top-level navigation).
625
+ * Useful for SSR-style integrations that import this from any context.
626
+ */
627
+ declare function initLivePreview(config?: LivePreviewClientConfig): LivePreviewClient | null;
628
+
629
+ /**
630
+ * Environment introspection helpers.
631
+ *
632
+ * The detection layer needs to know whether the runtime is currently
633
+ * running inside an iframe, in a popup, in development mode, and
634
+ * whether environment variables are accessible. None of these checks
635
+ * is dramatic on its own, but having a single, tested module avoids
636
+ * the duplicated, subtly-different `typeof process` snippets that
637
+ * accumulated in the legacy implementation.
638
+ *
639
+ * @module @detection/environment
640
+ */
641
+ /**
642
+ * Returns `true` when the current window is inside an iframe.
643
+ *
644
+ * Uses a try/catch around `window.self !== window.top` because the
645
+ * comparison throws in cross-origin iframes — and that very throw is
646
+ * itself a definitive "yes, I am in an iframe" signal.
647
+ */
648
+ declare function isInIframe(): boolean;
649
+ /**
650
+ * Returns `true` when the current window was opened via `window.open()`
651
+ * and the opener reference is still accessible.
652
+ */
653
+ declare function isInPopup(): boolean;
654
+ /**
655
+ * Returns `true` when running in a preview-capable context: iframe or
656
+ * popup. Anything else (top-level navigation) should skip live preview
657
+ * initialisation entirely.
658
+ */
659
+ declare function isInPreviewContext(): boolean;
660
+ /**
661
+ * Returns `true` when the runtime appears to be in development mode.
662
+ *
663
+ * Detection order:
664
+ * 1. `process.env.NODE_ENV !== 'production'`
665
+ * 2. `import.meta.env.DEV === true` (Vite / Astro / SvelteKit)
666
+ * 3. `window.location.hostname` is `localhost`/`127.0.0.1`
667
+ *
668
+ * Returns `false` when none of those are available.
669
+ */
670
+ declare function isDevMode(): boolean;
671
+
672
+ /**
673
+ * Origin detection and matching.
674
+ *
675
+ * Builds the set of trusted Payload admin origins and exposes a fast
676
+ * matcher used by the message bus. Three sources, in precedence order:
677
+ *
678
+ * 1. Explicit origins passed via configuration. Always trusted.
679
+ * 2. `document.referrer` — **only when no explicit origins are
680
+ * configured**. The browser writes the embedder's origin here
681
+ * when the iframe loads; using it as a fallback gives a
682
+ * zero-config path, but it must never widen an explicit
683
+ * allow-list (the referrer is whoever framed the page, which in
684
+ * an attack is the attacker).
685
+ * 3. Localhost pattern (any port on `localhost` / `127.0.0.1`),
686
+ * enabled when development mode is detected. Replaces the
687
+ * legacy hand-rolled port list.
688
+ *
689
+ * Once the parent's origin is confirmed by an accepted data-bearing
690
+ * update, callers can call `lockOrigin()` to drop every other candidate,
691
+ * making subsequent matches exact. A data-less `ready` handshake does
692
+ * not lock the detector.
693
+ *
694
+ * @module @detection/origin
695
+ */
696
+ interface OriginDetectorOptions {
697
+ /** Explicit additional origins to trust. Always allowed. */
698
+ readonly additionalOrigins?: readonly string[];
699
+ /** Set to `false` to opt out of `document.referrer` detection. */
700
+ readonly enableReferrerDetection?: boolean;
701
+ /** Set to `false` to opt out of localhost-pattern matching. */
702
+ readonly enableLocalhostMatching?: boolean;
703
+ /**
704
+ * Override the dev-mode detection. When `undefined`, dev mode is
705
+ * inferred from the runtime (see `isDevMode`).
706
+ */
707
+ readonly forceDevMode?: boolean;
708
+ /** Override `document.referrer` for tests. */
709
+ readonly referrer?: string;
710
+ }
711
+ /**
712
+ * Holds the resolved origin policy and exposes a matcher.
713
+ *
714
+ * The detector is stateful: it starts with the union of all candidate
715
+ * sources, then narrows down to a single, verified origin once the
716
+ * lifecycle calls `lockOrigin()` for its first accepted data-bearing
717
+ * update.
718
+ */
719
+ declare class OriginDetector {
720
+ #private;
721
+ constructor(options?: OriginDetectorOptions);
722
+ /**
723
+ * Returns `true` when `origin` is trusted under the current policy.
724
+ *
725
+ * If an origin has been locked, only that exact value matches.
726
+ * Otherwise explicit origins and (in dev mode) localhost-pattern
727
+ * matches are accepted. The referrer-derived origin is a **fallback,
728
+ * not a union member**: it only counts when NO explicit origins are
729
+ * configured. `document.referrer` always names whoever actually
730
+ * framed the page — if it were unioned with the explicit list, any
731
+ * site embedding the page in an iframe would become a trusted
732
+ * postMessage sender despite the consumer having pinned the admin
733
+ * origin explicitly.
734
+ */
735
+ matches(origin: string): boolean;
736
+ /**
737
+ * Narrow the trusted set to a single origin after an accepted
738
+ * data-bearing update. Subsequent calls to `matches()` only allow
739
+ * `origin`.
740
+ *
741
+ * Returns `true` if locking succeeded; `false` if the candidate is
742
+ * not currently considered trusted (which would defeat the purpose
743
+ * of locking).
744
+ */
745
+ lockOrigin(origin: string): boolean;
746
+ /**
747
+ * Release the origin lock so any allow-listed origin is trusted
748
+ * again until the next accepted data-bearing update. The lifecycle
749
+ * calls this on heartbeat timeout — after the trusted parent disappears,
750
+ * a different (still-allowed) origin should be free to reconnect.
751
+ *
752
+ * Returns the previously-locked origin, or `undefined` when nothing
753
+ * was locked.
754
+ */
755
+ unlockOrigin(): string | undefined;
756
+ /** The currently locked origin, or `undefined`. */
757
+ get lockedOrigin(): string | undefined;
758
+ /**
759
+ * Snapshot of every origin currently allowed by the policy. Used by
760
+ * the message bus to broadcast `ready` messages.
761
+ *
762
+ * Localhost pattern matches are expanded into the common dev ports
763
+ * so the handshake can find a Payload running on its usual port.
764
+ */
765
+ enumerate(): string[];
766
+ /**
767
+ * Reports whether `document.referrer` contributed at least one
768
+ * origin. Useful for surfacing a console warning when running in
769
+ * production without an explicit origin and the referrer is absent
770
+ * (because of a `Referrer-Policy: no-referrer` header).
771
+ */
772
+ get referrerWasAvailable(): boolean;
773
+ /**
774
+ * Indicates whether the detector is operating in a "production-without-
775
+ * explicit-origin" configuration. When `true`, the host should log a
776
+ * loud warning — silent connection failure is the worst outcome.
777
+ */
778
+ get isProductionUnconfigured(): boolean;
779
+ /**
780
+ * `true` when the referrer is the ONLY trust source (no explicit
781
+ * origins, no localhost matching). In that mode any site that frames
782
+ * the page becomes a trusted postMessage sender — `document.referrer`
783
+ * always names the actual embedder. Content is still sanitized, but
784
+ * the origin allow-list no longer restricts who may drive it. Hosts
785
+ * should surface a warning suggesting explicit `allowedOrigins` and
786
+ * a `frame-ancestors` policy in production.
787
+ */
788
+ get isReferrerOnlyTrust(): boolean;
789
+ }
790
+
791
+ /**
792
+ * Locale detection.
793
+ *
794
+ * The runtime needs a sensible default locale for date/number renders
795
+ * before Payload tells us the active locale. Order of precedence:
796
+ *
797
+ * 1. The `lang` attribute on `<html>`.
798
+ * 2. `navigator.language`.
799
+ * 3. The hard-coded fallback `'en'`.
800
+ *
801
+ * The previous version of this library defaulted to `'de'` — a German
802
+ * default that surprised consumers in other locales. We now use the
803
+ * browser hint and fall back to English.
804
+ *
805
+ * @module @detection/locale
806
+ */
807
+ /**
808
+ * Returns a BCP-47 locale string suitable for `Intl.*` constructors.
809
+ *
810
+ * Always returns a non-empty string. When no signal is available the
811
+ * fallback (`'en'`) is returned.
812
+ */
813
+ declare function detectInitialLocale(): string;
814
+
815
+ /**
816
+ * Single source of truth for the library version.
817
+ *
818
+ * Both the npm package surface (`src/index.ts`, `src/core-entry.ts`)
819
+ * and the inline runtime (`src/core/runtime.ts`, which gets bundled by
820
+ * esbuild and embedded into `runtime.generated.ts`) read the version
821
+ * from here so they never drift.
822
+ *
823
+ * Imports `package.json` directly — tsc, tsup, and esbuild all inline
824
+ * the value at build time when `resolveJsonModule` is enabled.
825
+ *
826
+ * @module @/version
827
+ */
828
+ declare const VERSION: string;
829
+
830
+ /**
831
+ * HTML-escape primitives.
832
+ *
833
+ * Implementations follow the OWASP XSS Prevention Cheat Sheet: each
834
+ * special character is replaced with its decimal or hex entity. The
835
+ * functions are isomorphic — they have no dependency on the DOM and
836
+ * can run in any JavaScript environment (browser, Node, Deno, Bun).
837
+ *
838
+ * @module @security/escape
839
+ */
840
+ /**
841
+ * Escape every HTML-significant character in `text` so the result is
842
+ * safe to interpolate into any HTML context (element body or attribute).
843
+ *
844
+ * - Coerces non-strings via `String(value)`; pass `null`/`undefined`
845
+ * intentionally — they become `"null"`/`"undefined"`.
846
+ * - Cannot fail; returns `""` for the empty input.
847
+ */
848
+ declare function escapeHtml(text: string): string;
849
+ /**
850
+ * Escape characters that would break out of a double-quoted HTML
851
+ * attribute value. Stricter than `escapeHtml` for body content but
852
+ * preserves URL characters (`/`, `=`, etc.) that are common in
853
+ * `href` and `src` values.
854
+ *
855
+ * Only call this for values that have already been validated as safe
856
+ * URLs or trusted strings; it does NOT block dangerous protocols.
857
+ */
858
+ declare function escapeHtmlAttribute(value: string): string;
859
+
860
+ /**
861
+ * URL safety validation.
862
+ *
863
+ * Single source of truth for URL-protocol decisions used by both the
864
+ * inline runtime and the high-level client. The previous version of
865
+ * this library had two implementations that subtly disagreed; this
866
+ * module replaces both.
867
+ *
868
+ * Policy:
869
+ * - Allow only the protocols in `SAFE_PROTOCOLS`.
870
+ * - Reject every other protocol explicitly, including `javascript:`,
871
+ * `data:`, `vbscript:`, `file:`, `blob:`, and any custom scheme.
872
+ * - Empty strings are NOT considered safe (this is a tightening from
873
+ * the legacy behaviour and avoids producing `<a href="">` markup).
874
+ *
875
+ * @module @security/url-validator
876
+ */
877
+ /**
878
+ * Returns `true` only when `url` is one of the explicitly allowed forms:
879
+ *
880
+ * - Absolute URL with `http:`, `https:`, `mailto:`, or `tel:` protocol
881
+ * - Protocol-relative URL (`//example.com/...`)
882
+ * - Same-origin absolute path (`/foo`)
883
+ * - Hash or query fragment (`#section`, `?q=1`)
884
+ * - Plain relative path (`foo/bar`, `./foo`, `../foo`)
885
+ */
886
+ declare function isSafeUrl(url: unknown): boolean;
887
+ /**
888
+ * Returns `true` when the URL points to an external HTTP(S) origin.
889
+ * Protocol-relative URLs (`//example.com/...`) count as external —
890
+ * they resolve to another origin and need the same `noopener`
891
+ * hardening as absolute ones.
892
+ *
893
+ * Used by renderers to decide whether to add `target="_blank"` and the
894
+ * `noopener noreferrer` rel attributes to anchors. Inputs that fail
895
+ * `isSafeUrl()` always return `false`.
896
+ */
897
+ declare function isExternalHttpUrl(url: string): boolean;
898
+
899
+ interface SanitizeOptions {
900
+ /** Extra tags to allow beyond the built-in list. Lower-case, untrimmed. */
901
+ readonly additionalAllowedTags?: readonly string[];
902
+ /** Extra per-tag attributes to allow. Tag and attribute names must be lower-case. */
903
+ readonly additionalAllowedAttributes?: Readonly<Record<string, readonly string[]>>;
904
+ }
905
+ /**
906
+ * Minimal `Document`-shaped surface the sanitizer needs. Any DOM
907
+ * implementation (browser, jsdom, linkedom, happy-dom, parse5 + a
908
+ * thin adapter) that produces `<template>`-style elements works.
909
+ */
910
+ interface SanitizerDocument {
911
+ createElement: (tagName: string) => {
912
+ innerHTML: string;
913
+ readonly content: ParentNode;
914
+ };
915
+ }
916
+ /**
917
+ * Inject a `Document` implementation for the sanitizer to use when
918
+ * `globalThis.document` is unavailable.
919
+ *
920
+ * Server-side renderers (Node, Bun, Deno without DOM globals) can wire
921
+ * this up once at startup with any of the popular pure-JS DOM
922
+ * libraries:
923
+ *
924
+ * ```ts
925
+ * // linkedom — recommended (smallest, fastest)
926
+ * import { parseHTML } from 'linkedom';
927
+ * import { setSanitizerDocument } from 'payload-live-preview';
928
+ * const { document } = parseHTML('<!doctype html><html><body></body></html>');
929
+ * setSanitizerDocument(document);
930
+ * ```
931
+ *
932
+ * ```ts
933
+ * // jsdom — heavier, but already in many SSR pipelines
934
+ * import { JSDOM } from 'jsdom';
935
+ * setSanitizerDocument(new JSDOM().window.document);
936
+ * ```
937
+ *
938
+ * Pass `null` to clear a previous override (mostly useful for tests).
939
+ */
940
+ declare function setSanitizerDocument(doc: SanitizerDocument | null): void;
941
+ /**
942
+ * Sanitize `html` and return safe HTML.
943
+ *
944
+ * The function never returns `undefined`/`null`. On malformed input the
945
+ * browser parser silently recovers and the sanitizer continues from
946
+ * the recovered tree.
947
+ *
948
+ * @throws {SanitizerEnvironmentError} when no DOM is available.
949
+ */
950
+ declare function sanitizeHtml(html: string, options?: SanitizeOptions): string;
951
+
952
+ /**
953
+ * Content-Security-Policy helpers.
954
+ *
955
+ * Two responsibilities:
956
+ *
957
+ * 1. Generate cryptographic nonces so consumers can opt into a strict
958
+ * `script-src 'nonce-...'` policy and avoid `'unsafe-inline'`.
959
+ * 2. Build the `frame-ancestors` directive value, restricting which
960
+ * origins may embed the preview page in an iframe.
961
+ *
962
+ * Both functions are isomorphic — they work in browsers, Node, and
963
+ * edge runtimes that expose Web Crypto via `globalThis.crypto`.
964
+ *
965
+ * @module @security/csp
966
+ */
967
+ /**
968
+ * Minimal Web-Crypto surface area we actually call. The full `Crypto`
969
+ * type drags in browser-only declarations that don't typecheck cleanly
970
+ * against Node's `webcrypto` shape — this narrower interface accepts
971
+ * both without an unsafe cast.
972
+ */
973
+ interface WebCryptoLike {
974
+ getRandomValues: <T extends ArrayBufferView | null>(array: T) => T;
975
+ }
976
+ /**
977
+ * Inject a Web Crypto implementation to use when `globalThis.crypto`
978
+ * is unavailable.
979
+ *
980
+ * Node.js exposed `globalThis.crypto` globally starting with v19. On
981
+ * Node 18 (still LTS at time of writing) consumers can flip the
982
+ * `--experimental-global-webcrypto` flag *or* call this function once
983
+ * at startup:
984
+ *
985
+ * ```ts
986
+ * import { webcrypto } from 'node:crypto';
987
+ * import { setCspCrypto } from 'payload-live-preview';
988
+ * setCspCrypto(webcrypto as { getRandomValues: typeof webcrypto.getRandomValues });
989
+ * ```
990
+ *
991
+ * Browser bundles never need to call this — `globalThis.crypto` is
992
+ * always present there, and the override only matters when the global
993
+ * is missing.
994
+ *
995
+ * Pass `null` to clear a previous override (mostly useful for tests).
996
+ */
997
+ declare function setCspCrypto(crypto: WebCryptoLike | null): void;
998
+ /**
999
+ * Generate a base64url-encoded cryptographic nonce.
1000
+ *
1001
+ * Uses `crypto.getRandomValues` everywhere — falls back to throwing
1002
+ * if no Web Crypto implementation is available, because a predictable
1003
+ * nonce would silently defeat the entire CSP.
1004
+ *
1005
+ * @param bytes Number of random bytes (defaults to 16 → 128-bit entropy).
1006
+ */
1007
+ declare function generateCspNonce(bytes?: number): string;
1008
+ interface FrameAncestorsOptions {
1009
+ readonly self?: boolean;
1010
+ readonly origins?: readonly string[];
1011
+ readonly allowNone?: boolean;
1012
+ }
1013
+ /**
1014
+ * Build the value for `frame-ancestors` in a `Content-Security-Policy`
1015
+ * header.
1016
+ *
1017
+ * Returns `'none'` if `allowNone: true` and no other sources are
1018
+ * provided. Otherwise concatenates `'self'` (if requested) with the
1019
+ * given origins, deduplicating and trimming entries.
1020
+ *
1021
+ * @example
1022
+ * `frame-ancestors ${buildFrameAncestors({ self: true, origins: [adminUrl] })}`
1023
+ */
1024
+ declare function buildFrameAncestors(options?: FrameAncestorsOptions): string;
1025
+ /**
1026
+ * Build a `script-src` value that permits a single nonce plus optional
1027
+ * extra sources.
1028
+ *
1029
+ * `'strict-dynamic'` is **opt-in**: under CSP 3 it makes browsers
1030
+ * ignore `'self'` and every host source in the directive, so any
1031
+ * parser-inserted script without a nonce (framework hydration scripts,
1032
+ * consumer `<script>` tags) would be blocked. Only enable it when every
1033
+ * script on the page carries the nonce.
1034
+ */
1035
+ declare function buildScriptSrcWithNonce(nonce: string, options?: {
1036
+ readonly self?: boolean;
1037
+ readonly extra?: readonly string[];
1038
+ readonly strictDynamic?: boolean;
1039
+ }): string;
1040
+
1041
+ /**
1042
+ * Protocol-version negotiation.
1043
+ *
1044
+ * ⚠️ Reality check: **stock Payload does not version its live-preview
1045
+ * postMessage protocol.** The admin never sends a `protocolVersion`
1046
+ * field and ignores extra fields in our `ready` handshake. Observed
1047
+ * wire behaviour:
1048
+ *
1049
+ * - Payload **2.x** sent `fieldSchemaJSON` with the first update
1050
+ * (client-side merge era).
1051
+ * - Payload **3.x** removed `fieldSchemaJSON` entirely; the official
1052
+ * client re-fetches merged documents through the REST API instead.
1053
+ * - No Payload version sends `previewToken` or any keepalive.
1054
+ *
1055
+ * This module therefore only matters for NON-stock admin panels — a
1056
+ * custom `window.postMessage` sender can advertise a version and this
1057
+ * library will negotiate `min(ours, theirs)` and expose capability
1058
+ * flags. Against stock Payload, `theirs` stays `undefined`, negotiation
1059
+ * collapses to v1, and nothing is gated on it: schema handling
1060
+ * activates whenever `fieldSchemaJSON` is actually present, and token
1061
+ * validation only runs when the consumer opts in.
1062
+ *
1063
+ * Version ladder (this library's own numbering, not Payload's):
1064
+ *
1065
+ * - **v1** — data + slug + locale (matches stock Payload of any era).
1066
+ * - **v2** — remote sends `fieldSchemaJSON` (Payload 2.x behaviour).
1067
+ * - **v3** — remote sends `previewToken` (custom senders only).
1068
+ * - **v4** — nested-array recursion / richer document events
1069
+ * (custom senders only).
1070
+ *
1071
+ * @module @core/protocol-version
1072
+ */
1073
+ declare const LIBRARY_PROTOCOL_VERSION = 4;
1074
+ /** Capability flag → minimum protocol version it requires. */
1075
+ declare const CAPABILITY_REQUIREMENTS: Readonly<Record<string, number>>;
1076
+ type ProtocolCapability = keyof typeof CAPABILITY_REQUIREMENTS;
1077
+ interface ProtocolNegotiation {
1078
+ /** This library's protocol version. */
1079
+ readonly ours: number;
1080
+ /** The remote party's version, when known. */
1081
+ readonly theirs: number | undefined;
1082
+ /** `min(ours, theirs ?? 1)` — the version both sides actually share. */
1083
+ readonly negotiated: number;
1084
+ /** Capability flags enabled at the negotiated version. */
1085
+ readonly capabilities: ReadonlySet<string>;
1086
+ }
1087
+ /**
1088
+ * Compute the negotiated view of the protocol.
1089
+ *
1090
+ * `theirs === undefined` collapses to v1 — assume the remote party is
1091
+ * an older Payload that does not advertise a version.
1092
+ *
1093
+ * Non-finite or non-positive `theirs` values fall back to v1 as well —
1094
+ * we never trust an obviously-corrupted negotiation byte.
1095
+ */
1096
+ declare function negotiateProtocol(theirs: number | undefined): ProtocolNegotiation;
1097
+ declare function hasCapability(negotiation: ProtocolNegotiation, capability: ProtocolCapability): boolean;
1098
+
1099
+ /**
1100
+ * Type-level path utilities — used by the typed binding DSL to give
1101
+ * callers compile-time autocomplete on field names.
1102
+ *
1103
+ * The same path strings the runtime reads from `data-payload-field`
1104
+ * are typed here, so a typo like `'heroTitlee'` is rejected at compile
1105
+ * time *and* the runtime side does not need to change.
1106
+ *
1107
+ * @module @dsl/paths
1108
+ */
1109
+ /**
1110
+ * All string-typed top-level keys of `T`. Non-string keys (numeric
1111
+ * indices on arrays) are filtered out.
1112
+ */
1113
+ type FieldName<T> = Extract<keyof T, string>;
1114
+ /**
1115
+ * Dotted paths into nested object structures, capped at three levels
1116
+ * deep — sufficient for Payload's `group`/`tab`/`array` nesting
1117
+ * without ballooning the IDE's IntelliSense list past the point of
1118
+ * usefulness.
1119
+ */
1120
+ 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 ? {
1121
+ [K in Extract<keyof T, string>]: K | (T[K] extends object ? `${K}.${FieldPath<T[K], Prev<Depth>>}` : never);
1122
+ }[Extract<keyof T, string>] : never;
1123
+ /** Decrement helper for depth-bounded recursion. */
1124
+ type Prev<N extends 0 | 1 | 2 | 3> = N extends 3 ? 2 : N extends 2 ? 1 : N extends 1 ? 0 : 0;
1125
+ /**
1126
+ * Resolve the value type at a dotted path. Returns `unknown` when the
1127
+ * path does not exist on `T`, so consumers can opt into stricter
1128
+ * `keyof`-based DSLs when they want absolute type safety.
1129
+ */
1130
+ 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;
1131
+
1132
+ /**
1133
+ * Typed binding helpers — emit the `data-payload-field` attribute pair
1134
+ * that the live-preview runtime reads, with compile-time validation
1135
+ * of the field name.
1136
+ *
1137
+ * Two forms are exposed:
1138
+ *
1139
+ * - **`bind<Schema>('heroTitle')`** — string-literal form. Lightest
1140
+ * possible runtime (no Proxy), but a rename in the IDE does NOT
1141
+ * follow into the string.
1142
+ * - **`bindByPath<Schema>(s => s.heroTitle)`** — proxy form. The
1143
+ * callback walks a Proxy that records the access path, returning
1144
+ * the same shape as `bind` so the two are interchangeable. Slightly
1145
+ * heavier runtime, but rename-safe and discourages typos.
1146
+ *
1147
+ * Both helpers also accept a richer return — `bind('image', 'src')` —
1148
+ * to bind an attribute other than the default `data-payload-field`,
1149
+ * matching the cache resolver's `data-payload-attribute` flow.
1150
+ *
1151
+ * @module @dsl/bind
1152
+ */
1153
+
1154
+ /**
1155
+ * Shape returned by every binding helper — a spreadable HTML attribute
1156
+ * record. Astro / JSX / Svelte all accept this style directly:
1157
+ *
1158
+ * ```astro
1159
+ * <h1 {...bind<Homepage>('heroTitle')}>{data.heroTitle}</h1>
1160
+ * ```
1161
+ */
1162
+ interface FieldBindingAttributes {
1163
+ readonly 'data-payload-field': string;
1164
+ readonly 'data-payload-attribute'?: string;
1165
+ readonly 'data-payload-type'?: string;
1166
+ }
1167
+ interface BindOptions {
1168
+ /** Override the bound attribute (e.g., `'src'` for `<img>`). Default: textContent. */
1169
+ readonly attribute?: string;
1170
+ /** Explicit field-type override — bypasses schema detection. */
1171
+ readonly type?: string;
1172
+ }
1173
+ /**
1174
+ * String-literal binding helper.
1175
+ *
1176
+ * ```ts
1177
+ * import { bind } from 'payload-live-preview';
1178
+ * import type { Homepage } from './payload-types';
1179
+ *
1180
+ * <h1 {...bind<Homepage>('heroTitle')}>{data.heroTitle}</h1>
1181
+ * <img {...bind<Homepage>('heroImage', { attribute: 'src' })} />
1182
+ * ```
1183
+ *
1184
+ * The generic `T` is the schema interface emitted by `pll-codegen`.
1185
+ * `field` is constrained to `keyof T` so misspellings fail at compile
1186
+ * time. When `T` is omitted, the helper accepts any string — useful
1187
+ * for incremental adoption.
1188
+ */
1189
+ declare function bind<T = Record<string, unknown>>(field: FieldName<T>, options?: BindOptions): FieldBindingAttributes;
1190
+ /**
1191
+ * Proxy-path binding helper.
1192
+ *
1193
+ * ```ts
1194
+ * <h1 {...bindByPath<Homepage>(d => d.heroTitle)}>{data.heroTitle}</h1>
1195
+ * ```
1196
+ *
1197
+ * The picker is invoked once with a recording Proxy; the recorded path
1198
+ * is joined with `.` and emitted as the binding name. Property access
1199
+ * on nested objects and arrays both work — `d => d.slides[0].title`
1200
+ * becomes `slides.title` (array indices are normalised away because the
1201
+ * runtime resolves bindings against schema, not against positional
1202
+ * indices).
1203
+ */
1204
+ declare function bindByPath<T = Record<string, unknown>>(picker: (data: T) => unknown, options?: BindOptions): FieldBindingAttributes;
2
1205
 
3
1206
  /**
4
1207
  * Narrow public entry — the minimum API for sites that only need to
@@ -7,18 +1210,18 @@ export { B as BindOptions, C as CachedElement, E as EventEmitter, b as EventHand
7
1210
  * Consumers who import from `payload-live-preview/core`
8
1211
  * get a bundle that **does not** include:
9
1212
  *
10
- * - the Lexical rich-text renderer (`@lexical/*`)
11
- * - the built-in plugins (`@plugins/built-in/*`)
1213
+ * - the built-in plugin constructors (`@plugins/built-in/*`)
12
1214
  * - the inline-script generator and the embedded runtime source
13
1215
  * - the framework adapters
14
1216
  *
15
- * These pieces add weight without being needed on a typical
16
- * production page. The full entry (`payload-live-preview`)
17
- * still re-exports everything for users who want the convenience.
1217
+ * The client still includes its built-in field renderers, including Lexical
1218
+ * rendering. The omitted exports add weight without being needed on a typical
1219
+ * production page. The full entry (`payload-live-preview`) re-exports them for
1220
+ * users who want the convenience.
18
1221
  *
19
1222
  * @packageDocumentation
20
1223
  */
21
1224
 
22
1225
  declare const CORE_ENTRY = true;
23
1226
 
24
- export { CORE_ENTRY };
1227
+ export { type BindOptions, CORE_ENTRY, type CachedElement, EventEmitter, type EventHandler, type FieldBindingAttributes, type FieldName, type FieldPath, type FieldRenderer, type FieldType, type FrameAncestorsOptions, LIBRARY_PROTOCOL_VERSION, LivePreviewClient, type LivePreviewClientConfig, type LivePreviewEventMap, OriginDetector, type PayloadFieldSchema, type PayloadFieldType, type PayloadLivePreviewData, type PayloadLivePreviewMessage, type ProtocolCapability, type ProtocolNegotiation, type RenderContext, type SanitizerDocument, type Unsubscribe, VERSION, type ValueAt, bind, bindByPath, buildFrameAncestors, buildScriptSrcWithNonce, detectInitialLocale, escapeHtml, escapeHtmlAttribute, generateCspNonce, hasCapability, initLivePreview, isDevMode, isExternalHttpUrl, isInIframe, isInPopup, isInPreviewContext, isSafeUrl, negotiateProtocol, sanitizeHtml, setCspCrypto, setSanitizerDocument };