payload-live-preview 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.d.cts CHANGED
@@ -1,3 +1,141 @@
1
+ /**
2
+ * Connection state + heartbeat.
3
+ *
4
+ * Tracks whether the runtime is currently receiving updates from a
5
+ * trusted origin and fires a callback when the heartbeat times out.
6
+ *
7
+ * The heartbeat is reset on every valid message and considered dead
8
+ * after `timeoutMs` of silence. The host typically responds by
9
+ * re-sending the `ready` handshake to attempt reconnection.
10
+ *
11
+ * @module @core/state
12
+ */
13
+ type ConnectionStatus = 'disconnected' | 'connecting' | 'connected';
14
+
15
+ /**
16
+ * The shape of a live-preview inspection snapshot.
17
+ *
18
+ * This module is types only. The snapshot is assembled by
19
+ * `LivePreviewRuntime.inspect()` as a plain object literal, because the inline
20
+ * runtime pays for every byte of indirection and the assembly needs access to
21
+ * internal state the runtime does not otherwise expose.
22
+ *
23
+ * A snapshot is a read of what the runtime already holds. It discloses nothing
24
+ * that is not already observable on the page: the trusted origins are inside
25
+ * the injected script, the field names are `data-payload-field` attributes in
26
+ * the DOM, and the counters are derived from those. It is therefore available
27
+ * in production rather than gated to development builds — a preview that
28
+ * misbehaves only on the deployed site is exactly the case where the
29
+ * information is worth having, and a development-only gate would withhold it
30
+ * there.
31
+ *
32
+ * Nothing here is transmitted anywhere. Reading a snapshot performs no I/O.
33
+ *
34
+ * @module @core/inspection/types
35
+ */
36
+
37
+ /** Origin trust as the runtime currently sees it. */
38
+ interface InspectionOrigins {
39
+ /**
40
+ * Origins the runtime is willing to accept messages from, as configured.
41
+ * An empty list means no explicit allow-list was given, which is the
42
+ * condition under which referrer and localhost heuristics decide instead.
43
+ */
44
+ readonly trusted: readonly string[];
45
+ /**
46
+ * The origin the runtime locked onto after its first accepted update, or
47
+ * `undefined` while still unlocked. Once locked, every other origin is
48
+ * refused for the rest of the session.
49
+ */
50
+ readonly locked: string | undefined;
51
+ }
52
+ /** Protocol versions and the capabilities they enable. */
53
+ interface InspectionProtocol {
54
+ /** This library's protocol version. */
55
+ readonly ours: number;
56
+ /** The remote party's version, or `undefined` if it never announced one. */
57
+ readonly theirs: number | undefined;
58
+ /** `min(ours, theirs ?? 1)` — what both sides actually share. */
59
+ readonly negotiated: number;
60
+ /** Capability flags enabled at the negotiated version. */
61
+ readonly capabilities: readonly string[];
62
+ }
63
+ /** Revision accounting for the update pipeline. */
64
+ interface InspectionRevisions {
65
+ /** Updates accepted since the runtime started. */
66
+ readonly accepted: number;
67
+ /**
68
+ * Updates that were still in flight when a newer one arrived and were
69
+ * therefore abandoned. A number that tracks `accepted` closely means the
70
+ * editor is out-typing the pipeline, which is normal; it is only a problem
71
+ * when the last update is among the abandoned ones.
72
+ */
73
+ readonly superseded: number;
74
+ /** Revision currently in flight, or `undefined` when the pipeline is idle. */
75
+ readonly active: number | undefined;
76
+ }
77
+ /** What the DOM offers the runtime to write into. */
78
+ interface InspectionBindings {
79
+ /** Elements carrying a binding. */
80
+ readonly elements: number;
81
+ /** Distinct field names across those elements. */
82
+ readonly fields: number;
83
+ /** Those field names, sorted. */
84
+ readonly fieldNames: readonly string[];
85
+ /**
86
+ * Field names that arrived in an update but matched no binding, as far as
87
+ * the runtime has observed. This is the list to read when a field refuses
88
+ * to update: a name here is a markup problem, a name absent from here and
89
+ * from `fieldNames` was never sent.
90
+ */
91
+ readonly orphanFields: readonly string[];
92
+ /** Whether `scopeBindingsByOwner` is active. */
93
+ readonly ownerScoped: boolean;
94
+ /** Distinct document owners currently on the page, sorted. */
95
+ readonly owners: readonly string[];
96
+ }
97
+ /** Scheduler work in progress. */
98
+ interface InspectionScheduler {
99
+ /** Writes buffered for the next flush. */
100
+ readonly pending: number;
101
+ /**
102
+ * Writes held back because their element is offscreen and the binding count
103
+ * is above `visibilityGateThreshold`. These are applied when the element
104
+ * scrolls into view — never, on a page nobody scrolls.
105
+ */
106
+ readonly deferred: number;
107
+ /** The binding count above which the visibility gate starts deferring. */
108
+ readonly visibilityGateThreshold: number;
109
+ /** Whether the binding count is above that threshold right now. */
110
+ readonly visibilityGateActive: boolean;
111
+ /** Stats of the most recent flush, or `undefined` if none has run. */
112
+ readonly lastFlush: {
113
+ readonly applied: number;
114
+ readonly deferred: number;
115
+ readonly durationMs: number;
116
+ } | undefined;
117
+ }
118
+ /**
119
+ * A point-in-time read of runtime state, for diagnosing a preview that is not
120
+ * behaving. Every field is a plain value; the snapshot holds no references
121
+ * into runtime internals and does not change after it is returned.
122
+ */
123
+ interface LivePreviewInspection {
124
+ /** Package version that produced this snapshot. */
125
+ readonly version: string;
126
+ /** Whether the runtime is started, and whether it has been destroyed. */
127
+ readonly started: boolean;
128
+ /** Connection status as reported to the `connect`/`disconnect` events. */
129
+ readonly status: ConnectionStatus;
130
+ readonly origins: InspectionOrigins;
131
+ readonly protocol: InspectionProtocol;
132
+ readonly revisions: InspectionRevisions;
133
+ readonly bindings: InspectionBindings;
134
+ readonly scheduler: InspectionScheduler;
135
+ /** Field types with a registered renderer, sorted. */
136
+ readonly renderers: readonly string[];
137
+ }
138
+
1
139
  /**
2
140
  * Payload Live Preview message protocol.
3
141
  *
@@ -113,6 +251,101 @@ type PayloadFieldType = 'text' | 'textarea' | 'richText' | 'email' | 'number' |
113
251
  */
114
252
  type PayloadFieldCondition = (data: unknown, siblingData: unknown) => boolean;
115
253
 
254
+ /**
255
+ * Stable identifiers for everything the runtime reports.
256
+ *
257
+ * Prose gets reworded; a code does not. A consumer filtering logs, an alert
258
+ * rule, or a bug report referring to `LP0301` keeps meaning the same thing
259
+ * after the sentence around it is rewritten, and a code is greppable in a way
260
+ * that a sentence fragment is not.
261
+ *
262
+ * Codes are grouped by the question they answer:
263
+ *
264
+ * - `LP01xx` — configuration and origin trust: the runtime is running, but it
265
+ * was set up in a way that will bite.
266
+ * - `LP02xx` — bindings and markup: an update had nowhere to land.
267
+ * - `LP03xx` — updates and scheduling: the update landed somewhere other than
268
+ * the DOM, or not yet.
269
+ * - `LP04xx` — rendering: a value reached an element and was refused.
270
+ * - `LP05xx` — protocol and messages: something arrived that was not accepted.
271
+ * - `LP06xx` — consumer callbacks: code the consumer supplied threw.
272
+ * - `LP07xx` — audit findings from `pll doctor`, which reports on a served
273
+ * response rather than on a running runtime.
274
+ *
275
+ * A code is part of the public contract. Codes are never reused for a
276
+ * different meaning and never renumbered; a retired code stays retired.
277
+ * `LP0604` is unassigned: a throwing token validator is deliberately treated
278
+ * as a rejection and reported as `LP0502`, so there is nothing distinct to
279
+ * report yet. The number stays reserved rather than being handed to something
280
+ * else.
281
+ *
282
+ * The runtime writes these as string literals at each reporting site rather
283
+ * than reading them from this record, so the inline build pays for the six
284
+ * characters it prints and nothing more. This record exists so consumers can
285
+ * refer to a code by name instead of copying a literal.
286
+ *
287
+ * @module @core/diagnostic-codes
288
+ */
289
+ /**
290
+ * Every diagnostic code the runtime can report, by name.
291
+ *
292
+ * ```ts
293
+ * client.events.on('error', (e) => {
294
+ * if (e.code === DIAGNOSTIC_CODES.TransformThrew) reportToSentry(e.error);
295
+ * });
296
+ * ```
297
+ */
298
+ declare const DIAGNOSTIC_CODES: Readonly<{
299
+ /** No trusted origin configured in production; nothing will be accepted. */
300
+ readonly NoTrustedOrigin: "LP0101";
301
+ /** Origin trust rests on `document.referrer`, so any framing site is trusted. */
302
+ readonly ReferrerOnlyTrust: "LP0102";
303
+ /** An update named a field with no binding anchor on the page. */
304
+ readonly OrphanField: "LP0201";
305
+ /** Owner scoping is on and the update names no document it could belong to. */
306
+ readonly UnattributableUpdate: "LP0202";
307
+ /** The visibility gate held offscreen writes back until they scroll into view. */
308
+ readonly VisibilityGateDeferred: "LP0301";
309
+ /** A value was refused because the attribute or the value itself is unsafe. */
310
+ readonly UnsafeAttributeWrite: "LP0401";
311
+ /** A text element has structured children, so replacing its text was skipped. */
312
+ readonly TextTargetHasChildren: "LP0402";
313
+ /** A structural container has no array template, so the update was skipped. */
314
+ readonly MissingArrayTemplate: "LP0403";
315
+ /** A message was rejected before it reached the update pipeline. */
316
+ readonly MessageRejected: "LP0501";
317
+ /** A preview token was rejected. */
318
+ readonly TokenRejected: "LP0502";
319
+ /** A consumer event handler threw. */
320
+ readonly HandlerThrew: "LP0601";
321
+ /** A consumer transform threw; the original value was kept. */
322
+ readonly TransformThrew: "LP0602";
323
+ /** A renderer threw while writing a value. */
324
+ readonly RendererThrew: "LP0603";
325
+ /** Runtime startup failed. */
326
+ readonly StartupFailed: "LP0605";
327
+ /** Sending the ready handshake failed. */
328
+ readonly ReadyFailed: "LP0606";
329
+ /** The audit found no runtime in a response that carried preview intent. */
330
+ readonly AuditRuntimeMissing: "LP0701";
331
+ /** The preview response declares no `frame-ancestors`. */
332
+ readonly AuditNoFrameAncestors: "LP0702";
333
+ /** `X-Frame-Options` forbids framing, which no CSP can undo. */
334
+ readonly AuditFrameOptionsBlocks: "LP0703";
335
+ /** Binding attributes are served to anonymous visitors. */
336
+ readonly AuditBindingsExposed: "LP0704";
337
+ /** More bindings than the default visibility gate will write eagerly. */
338
+ readonly AuditGateThresholdExceeded: "LP0705";
339
+ /** Owner markers exist, but some bindings are outside all of them. */
340
+ readonly AuditUnownedBindings: "LP0706";
341
+ /** The preview response carries no bindings at all. */
342
+ readonly AuditNoBindings: "LP0707";
343
+ /** The URL did not return an HTML page, so nothing else can be judged. */
344
+ readonly AuditNotAPage: "LP0708";
345
+ }>;
346
+ /** A diagnostic code, as it appears in log output and on the `error` event. */
347
+ type DiagnosticCode = (typeof DIAGNOSTIC_CODES)[keyof typeof DIAGNOSTIC_CODES];
348
+
116
349
  /**
117
350
  * Public event payload shapes for the live preview lifecycle.
118
351
  *
@@ -193,11 +426,14 @@ interface LivePreviewEventMap {
193
426
  /**
194
427
  * Fired on errors that the runtime caught but cannot fully recover from.
195
428
  *
196
- * The `context` string identifies where the error originated.
429
+ * The `context` string identifies where the error originated. `code` says
430
+ * the same thing in a form that survives rewording — branch on `code`, read
431
+ * `context` when you want the human-readable origin.
197
432
  */
198
433
  readonly error: {
199
434
  readonly error: Error;
200
435
  readonly context: string;
436
+ readonly code: DiagnosticCode;
201
437
  };
202
438
  /**
203
439
  * Fired during destroy after browser observers and the message listener are
@@ -657,6 +893,15 @@ declare class LivePreviewClient {
657
893
  /** Current connection status. */
658
894
  get status(): 'disconnected' | 'connecting' | 'connected';
659
895
  /** Number of valid updates received so far. */
896
+ /**
897
+ * Point-in-time read of runtime state, for diagnosing a preview that is not
898
+ * updating. Performs no I/O and transmits nothing.
899
+ *
900
+ * Read `bindings.orphanFields` when a field refuses to update, and
901
+ * `scheduler.deferred` together with `scheduler.visibilityGateActive` when
902
+ * updates stop below the fold.
903
+ */
904
+ inspect(): LivePreviewInspection;
660
905
  get updateCount(): number;
661
906
  /** `true` once `destroy()` has been called. */
662
907
  get destroyed(): boolean;
@@ -1364,4 +1609,4 @@ declare function createPreviewBindings(options: PreviewBindingsOptions): Preview
1364
1609
 
1365
1610
  declare const CORE_ENTRY = true;
1366
1611
 
1367
- 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 OwnerBindingAttributes, type PayloadFieldSchema, type PayloadFieldType, type PayloadLivePreviewData, type PayloadLivePreviewMessage, type PreviewBindings, type PreviewBindingsOptions, type ProtocolCapability, type ProtocolNegotiation, type RenderContext, type SanitizerDocument, type SuppressedBinding, type Unsubscribe, VERSION, type ValueAt, bind, bindByPath, buildFrameAncestors, buildScriptSrcWithNonce, createPreviewBindings, detectInitialLocale, escapeHtml, escapeHtmlAttribute, generateCspNonce, hasCapability, initLivePreview, isDevMode, isExternalHttpUrl, isInIframe, isInPopup, isInPreviewContext, isSafeUrl, negotiateProtocol, sanitizeHtml, setCspCrypto, setSanitizerDocument };
1612
+ export { type BindOptions, CORE_ENTRY, type CachedElement, type ConnectionStatus, DIAGNOSTIC_CODES, type DiagnosticCode, EventEmitter, type EventHandler, type FieldBindingAttributes, type FieldName, type FieldPath, type FieldRenderer, type FieldType, type FrameAncestorsOptions, type InspectionBindings, type InspectionOrigins, type InspectionProtocol, type InspectionRevisions, type InspectionScheduler, LIBRARY_PROTOCOL_VERSION, LivePreviewClient, type LivePreviewClientConfig, type LivePreviewEventMap, type LivePreviewInspection, OriginDetector, type OwnerBindingAttributes, type PayloadFieldSchema, type PayloadFieldType, type PayloadLivePreviewData, type PayloadLivePreviewMessage, type PreviewBindings, type PreviewBindingsOptions, type ProtocolCapability, type ProtocolNegotiation, type RenderContext, type SanitizerDocument, type SuppressedBinding, type Unsubscribe, VERSION, type ValueAt, bind, bindByPath, buildFrameAncestors, buildScriptSrcWithNonce, createPreviewBindings, detectInitialLocale, escapeHtml, escapeHtmlAttribute, generateCspNonce, hasCapability, initLivePreview, isDevMode, isExternalHttpUrl, isInIframe, isInPopup, isInPreviewContext, isSafeUrl, negotiateProtocol, sanitizeHtml, setCspCrypto, setSanitizerDocument };
package/dist/core.d.ts CHANGED
@@ -1,3 +1,141 @@
1
+ /**
2
+ * Connection state + heartbeat.
3
+ *
4
+ * Tracks whether the runtime is currently receiving updates from a
5
+ * trusted origin and fires a callback when the heartbeat times out.
6
+ *
7
+ * The heartbeat is reset on every valid message and considered dead
8
+ * after `timeoutMs` of silence. The host typically responds by
9
+ * re-sending the `ready` handshake to attempt reconnection.
10
+ *
11
+ * @module @core/state
12
+ */
13
+ type ConnectionStatus = 'disconnected' | 'connecting' | 'connected';
14
+
15
+ /**
16
+ * The shape of a live-preview inspection snapshot.
17
+ *
18
+ * This module is types only. The snapshot is assembled by
19
+ * `LivePreviewRuntime.inspect()` as a plain object literal, because the inline
20
+ * runtime pays for every byte of indirection and the assembly needs access to
21
+ * internal state the runtime does not otherwise expose.
22
+ *
23
+ * A snapshot is a read of what the runtime already holds. It discloses nothing
24
+ * that is not already observable on the page: the trusted origins are inside
25
+ * the injected script, the field names are `data-payload-field` attributes in
26
+ * the DOM, and the counters are derived from those. It is therefore available
27
+ * in production rather than gated to development builds — a preview that
28
+ * misbehaves only on the deployed site is exactly the case where the
29
+ * information is worth having, and a development-only gate would withhold it
30
+ * there.
31
+ *
32
+ * Nothing here is transmitted anywhere. Reading a snapshot performs no I/O.
33
+ *
34
+ * @module @core/inspection/types
35
+ */
36
+
37
+ /** Origin trust as the runtime currently sees it. */
38
+ interface InspectionOrigins {
39
+ /**
40
+ * Origins the runtime is willing to accept messages from, as configured.
41
+ * An empty list means no explicit allow-list was given, which is the
42
+ * condition under which referrer and localhost heuristics decide instead.
43
+ */
44
+ readonly trusted: readonly string[];
45
+ /**
46
+ * The origin the runtime locked onto after its first accepted update, or
47
+ * `undefined` while still unlocked. Once locked, every other origin is
48
+ * refused for the rest of the session.
49
+ */
50
+ readonly locked: string | undefined;
51
+ }
52
+ /** Protocol versions and the capabilities they enable. */
53
+ interface InspectionProtocol {
54
+ /** This library's protocol version. */
55
+ readonly ours: number;
56
+ /** The remote party's version, or `undefined` if it never announced one. */
57
+ readonly theirs: number | undefined;
58
+ /** `min(ours, theirs ?? 1)` — what both sides actually share. */
59
+ readonly negotiated: number;
60
+ /** Capability flags enabled at the negotiated version. */
61
+ readonly capabilities: readonly string[];
62
+ }
63
+ /** Revision accounting for the update pipeline. */
64
+ interface InspectionRevisions {
65
+ /** Updates accepted since the runtime started. */
66
+ readonly accepted: number;
67
+ /**
68
+ * Updates that were still in flight when a newer one arrived and were
69
+ * therefore abandoned. A number that tracks `accepted` closely means the
70
+ * editor is out-typing the pipeline, which is normal; it is only a problem
71
+ * when the last update is among the abandoned ones.
72
+ */
73
+ readonly superseded: number;
74
+ /** Revision currently in flight, or `undefined` when the pipeline is idle. */
75
+ readonly active: number | undefined;
76
+ }
77
+ /** What the DOM offers the runtime to write into. */
78
+ interface InspectionBindings {
79
+ /** Elements carrying a binding. */
80
+ readonly elements: number;
81
+ /** Distinct field names across those elements. */
82
+ readonly fields: number;
83
+ /** Those field names, sorted. */
84
+ readonly fieldNames: readonly string[];
85
+ /**
86
+ * Field names that arrived in an update but matched no binding, as far as
87
+ * the runtime has observed. This is the list to read when a field refuses
88
+ * to update: a name here is a markup problem, a name absent from here and
89
+ * from `fieldNames` was never sent.
90
+ */
91
+ readonly orphanFields: readonly string[];
92
+ /** Whether `scopeBindingsByOwner` is active. */
93
+ readonly ownerScoped: boolean;
94
+ /** Distinct document owners currently on the page, sorted. */
95
+ readonly owners: readonly string[];
96
+ }
97
+ /** Scheduler work in progress. */
98
+ interface InspectionScheduler {
99
+ /** Writes buffered for the next flush. */
100
+ readonly pending: number;
101
+ /**
102
+ * Writes held back because their element is offscreen and the binding count
103
+ * is above `visibilityGateThreshold`. These are applied when the element
104
+ * scrolls into view — never, on a page nobody scrolls.
105
+ */
106
+ readonly deferred: number;
107
+ /** The binding count above which the visibility gate starts deferring. */
108
+ readonly visibilityGateThreshold: number;
109
+ /** Whether the binding count is above that threshold right now. */
110
+ readonly visibilityGateActive: boolean;
111
+ /** Stats of the most recent flush, or `undefined` if none has run. */
112
+ readonly lastFlush: {
113
+ readonly applied: number;
114
+ readonly deferred: number;
115
+ readonly durationMs: number;
116
+ } | undefined;
117
+ }
118
+ /**
119
+ * A point-in-time read of runtime state, for diagnosing a preview that is not
120
+ * behaving. Every field is a plain value; the snapshot holds no references
121
+ * into runtime internals and does not change after it is returned.
122
+ */
123
+ interface LivePreviewInspection {
124
+ /** Package version that produced this snapshot. */
125
+ readonly version: string;
126
+ /** Whether the runtime is started, and whether it has been destroyed. */
127
+ readonly started: boolean;
128
+ /** Connection status as reported to the `connect`/`disconnect` events. */
129
+ readonly status: ConnectionStatus;
130
+ readonly origins: InspectionOrigins;
131
+ readonly protocol: InspectionProtocol;
132
+ readonly revisions: InspectionRevisions;
133
+ readonly bindings: InspectionBindings;
134
+ readonly scheduler: InspectionScheduler;
135
+ /** Field types with a registered renderer, sorted. */
136
+ readonly renderers: readonly string[];
137
+ }
138
+
1
139
  /**
2
140
  * Payload Live Preview message protocol.
3
141
  *
@@ -113,6 +251,101 @@ type PayloadFieldType = 'text' | 'textarea' | 'richText' | 'email' | 'number' |
113
251
  */
114
252
  type PayloadFieldCondition = (data: unknown, siblingData: unknown) => boolean;
115
253
 
254
+ /**
255
+ * Stable identifiers for everything the runtime reports.
256
+ *
257
+ * Prose gets reworded; a code does not. A consumer filtering logs, an alert
258
+ * rule, or a bug report referring to `LP0301` keeps meaning the same thing
259
+ * after the sentence around it is rewritten, and a code is greppable in a way
260
+ * that a sentence fragment is not.
261
+ *
262
+ * Codes are grouped by the question they answer:
263
+ *
264
+ * - `LP01xx` — configuration and origin trust: the runtime is running, but it
265
+ * was set up in a way that will bite.
266
+ * - `LP02xx` — bindings and markup: an update had nowhere to land.
267
+ * - `LP03xx` — updates and scheduling: the update landed somewhere other than
268
+ * the DOM, or not yet.
269
+ * - `LP04xx` — rendering: a value reached an element and was refused.
270
+ * - `LP05xx` — protocol and messages: something arrived that was not accepted.
271
+ * - `LP06xx` — consumer callbacks: code the consumer supplied threw.
272
+ * - `LP07xx` — audit findings from `pll doctor`, which reports on a served
273
+ * response rather than on a running runtime.
274
+ *
275
+ * A code is part of the public contract. Codes are never reused for a
276
+ * different meaning and never renumbered; a retired code stays retired.
277
+ * `LP0604` is unassigned: a throwing token validator is deliberately treated
278
+ * as a rejection and reported as `LP0502`, so there is nothing distinct to
279
+ * report yet. The number stays reserved rather than being handed to something
280
+ * else.
281
+ *
282
+ * The runtime writes these as string literals at each reporting site rather
283
+ * than reading them from this record, so the inline build pays for the six
284
+ * characters it prints and nothing more. This record exists so consumers can
285
+ * refer to a code by name instead of copying a literal.
286
+ *
287
+ * @module @core/diagnostic-codes
288
+ */
289
+ /**
290
+ * Every diagnostic code the runtime can report, by name.
291
+ *
292
+ * ```ts
293
+ * client.events.on('error', (e) => {
294
+ * if (e.code === DIAGNOSTIC_CODES.TransformThrew) reportToSentry(e.error);
295
+ * });
296
+ * ```
297
+ */
298
+ declare const DIAGNOSTIC_CODES: Readonly<{
299
+ /** No trusted origin configured in production; nothing will be accepted. */
300
+ readonly NoTrustedOrigin: "LP0101";
301
+ /** Origin trust rests on `document.referrer`, so any framing site is trusted. */
302
+ readonly ReferrerOnlyTrust: "LP0102";
303
+ /** An update named a field with no binding anchor on the page. */
304
+ readonly OrphanField: "LP0201";
305
+ /** Owner scoping is on and the update names no document it could belong to. */
306
+ readonly UnattributableUpdate: "LP0202";
307
+ /** The visibility gate held offscreen writes back until they scroll into view. */
308
+ readonly VisibilityGateDeferred: "LP0301";
309
+ /** A value was refused because the attribute or the value itself is unsafe. */
310
+ readonly UnsafeAttributeWrite: "LP0401";
311
+ /** A text element has structured children, so replacing its text was skipped. */
312
+ readonly TextTargetHasChildren: "LP0402";
313
+ /** A structural container has no array template, so the update was skipped. */
314
+ readonly MissingArrayTemplate: "LP0403";
315
+ /** A message was rejected before it reached the update pipeline. */
316
+ readonly MessageRejected: "LP0501";
317
+ /** A preview token was rejected. */
318
+ readonly TokenRejected: "LP0502";
319
+ /** A consumer event handler threw. */
320
+ readonly HandlerThrew: "LP0601";
321
+ /** A consumer transform threw; the original value was kept. */
322
+ readonly TransformThrew: "LP0602";
323
+ /** A renderer threw while writing a value. */
324
+ readonly RendererThrew: "LP0603";
325
+ /** Runtime startup failed. */
326
+ readonly StartupFailed: "LP0605";
327
+ /** Sending the ready handshake failed. */
328
+ readonly ReadyFailed: "LP0606";
329
+ /** The audit found no runtime in a response that carried preview intent. */
330
+ readonly AuditRuntimeMissing: "LP0701";
331
+ /** The preview response declares no `frame-ancestors`. */
332
+ readonly AuditNoFrameAncestors: "LP0702";
333
+ /** `X-Frame-Options` forbids framing, which no CSP can undo. */
334
+ readonly AuditFrameOptionsBlocks: "LP0703";
335
+ /** Binding attributes are served to anonymous visitors. */
336
+ readonly AuditBindingsExposed: "LP0704";
337
+ /** More bindings than the default visibility gate will write eagerly. */
338
+ readonly AuditGateThresholdExceeded: "LP0705";
339
+ /** Owner markers exist, but some bindings are outside all of them. */
340
+ readonly AuditUnownedBindings: "LP0706";
341
+ /** The preview response carries no bindings at all. */
342
+ readonly AuditNoBindings: "LP0707";
343
+ /** The URL did not return an HTML page, so nothing else can be judged. */
344
+ readonly AuditNotAPage: "LP0708";
345
+ }>;
346
+ /** A diagnostic code, as it appears in log output and on the `error` event. */
347
+ type DiagnosticCode = (typeof DIAGNOSTIC_CODES)[keyof typeof DIAGNOSTIC_CODES];
348
+
116
349
  /**
117
350
  * Public event payload shapes for the live preview lifecycle.
118
351
  *
@@ -193,11 +426,14 @@ interface LivePreviewEventMap {
193
426
  /**
194
427
  * Fired on errors that the runtime caught but cannot fully recover from.
195
428
  *
196
- * The `context` string identifies where the error originated.
429
+ * The `context` string identifies where the error originated. `code` says
430
+ * the same thing in a form that survives rewording — branch on `code`, read
431
+ * `context` when you want the human-readable origin.
197
432
  */
198
433
  readonly error: {
199
434
  readonly error: Error;
200
435
  readonly context: string;
436
+ readonly code: DiagnosticCode;
201
437
  };
202
438
  /**
203
439
  * Fired during destroy after browser observers and the message listener are
@@ -657,6 +893,15 @@ declare class LivePreviewClient {
657
893
  /** Current connection status. */
658
894
  get status(): 'disconnected' | 'connecting' | 'connected';
659
895
  /** Number of valid updates received so far. */
896
+ /**
897
+ * Point-in-time read of runtime state, for diagnosing a preview that is not
898
+ * updating. Performs no I/O and transmits nothing.
899
+ *
900
+ * Read `bindings.orphanFields` when a field refuses to update, and
901
+ * `scheduler.deferred` together with `scheduler.visibilityGateActive` when
902
+ * updates stop below the fold.
903
+ */
904
+ inspect(): LivePreviewInspection;
660
905
  get updateCount(): number;
661
906
  /** `true` once `destroy()` has been called. */
662
907
  get destroyed(): boolean;
@@ -1364,4 +1609,4 @@ declare function createPreviewBindings(options: PreviewBindingsOptions): Preview
1364
1609
 
1365
1610
  declare const CORE_ENTRY = true;
1366
1611
 
1367
- 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 OwnerBindingAttributes, type PayloadFieldSchema, type PayloadFieldType, type PayloadLivePreviewData, type PayloadLivePreviewMessage, type PreviewBindings, type PreviewBindingsOptions, type ProtocolCapability, type ProtocolNegotiation, type RenderContext, type SanitizerDocument, type SuppressedBinding, type Unsubscribe, VERSION, type ValueAt, bind, bindByPath, buildFrameAncestors, buildScriptSrcWithNonce, createPreviewBindings, detectInitialLocale, escapeHtml, escapeHtmlAttribute, generateCspNonce, hasCapability, initLivePreview, isDevMode, isExternalHttpUrl, isInIframe, isInPopup, isInPreviewContext, isSafeUrl, negotiateProtocol, sanitizeHtml, setCspCrypto, setSanitizerDocument };
1612
+ export { type BindOptions, CORE_ENTRY, type CachedElement, type ConnectionStatus, DIAGNOSTIC_CODES, type DiagnosticCode, EventEmitter, type EventHandler, type FieldBindingAttributes, type FieldName, type FieldPath, type FieldRenderer, type FieldType, type FrameAncestorsOptions, type InspectionBindings, type InspectionOrigins, type InspectionProtocol, type InspectionRevisions, type InspectionScheduler, LIBRARY_PROTOCOL_VERSION, LivePreviewClient, type LivePreviewClientConfig, type LivePreviewEventMap, type LivePreviewInspection, OriginDetector, type OwnerBindingAttributes, type PayloadFieldSchema, type PayloadFieldType, type PayloadLivePreviewData, type PayloadLivePreviewMessage, type PreviewBindings, type PreviewBindingsOptions, type ProtocolCapability, type ProtocolNegotiation, type RenderContext, type SanitizerDocument, type SuppressedBinding, type Unsubscribe, VERSION, type ValueAt, bind, bindByPath, buildFrameAncestors, buildScriptSrcWithNonce, createPreviewBindings, detectInitialLocale, escapeHtml, escapeHtmlAttribute, generateCspNonce, hasCapability, initLivePreview, isDevMode, isExternalHttpUrl, isInIframe, isInPopup, isInPreviewContext, isSafeUrl, negotiateProtocol, sanitizeHtml, setCspCrypto, setSanitizerDocument };