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