payload-live-preview 1.1.0 → 1.2.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
@@ -1186,12 +1186,24 @@ interface FieldBindingAttributes {
1186
1186
  readonly 'data-payload-field': string;
1187
1187
  readonly 'data-payload-attribute'?: string;
1188
1188
  readonly 'data-payload-type'?: string;
1189
+ readonly 'data-payload-richtext'?: string;
1190
+ readonly 'data-payload-html'?: string;
1191
+ readonly 'data-payload-locale'?: string;
1189
1192
  }
1190
1193
  interface BindOptions {
1191
1194
  /** Override the bound attribute (e.g., `'src'` for `<img>`). Default: textContent. */
1192
1195
  readonly attribute?: string;
1193
1196
  /** Explicit field-type override — bypasses schema detection. */
1194
1197
  readonly type?: string;
1198
+ /**
1199
+ * Mark the binding as Lexical rich text. Usually detected automatically;
1200
+ * set it where the initial render is empty and there is nothing to detect.
1201
+ */
1202
+ readonly richtext?: boolean;
1203
+ /** Render the value as sanitised HTML rather than text. */
1204
+ readonly html?: boolean;
1205
+ /** Lock this element to one locale, overriding the message locale. */
1206
+ readonly locale?: string;
1195
1207
  }
1196
1208
  /**
1197
1209
  * String-literal binding helper.
@@ -1226,6 +1238,92 @@ declare function bind<T = Record<string, unknown>>(field: FieldName<T>, options?
1226
1238
  */
1227
1239
  declare function bindByPath<T = Record<string, unknown>>(picker: (data: T) => unknown, options?: BindOptions): FieldBindingAttributes;
1228
1240
 
1241
+ /**
1242
+ * Authorization-gated binding emission.
1243
+ *
1244
+ * Binding attributes are not neutral markup. `data-payload-field` names a CMS
1245
+ * field, and `data-payload-owner` names a global, a collection and often a
1246
+ * document id. Emitted unconditionally they publish the shape of the content
1247
+ * model — and the identity of documents — to every anonymous visitor and
1248
+ * crawler of the page.
1249
+ *
1250
+ * The gate belongs to the application, not to this package: it is the same
1251
+ * decision that already controls draft reads and cache policy. What this
1252
+ * module provides is a place to apply that decision **once per request** so no
1253
+ * individual call site can forget it, and an emission unit that cannot be
1254
+ * partially suppressed.
1255
+ *
1256
+ * That second property matters more than it looks. A binding is not one
1257
+ * attribute: a field travels with its type, its locale, its rich-text marker
1258
+ * and its owner. Gating only the field name leaves the companions behind,
1259
+ * which discloses the taxonomy anyway and leaves the runtime looking at a
1260
+ * binding whose field is gone.
1261
+ *
1262
+ * @module @dsl/preview-bindings
1263
+ */
1264
+
1265
+ /** Owner marker naming the document a subtree belongs to. */
1266
+ interface OwnerBindingAttributes {
1267
+ readonly 'data-payload-owner': string;
1268
+ }
1269
+ /**
1270
+ * The complete absence of binding attributes.
1271
+ *
1272
+ * Spreading this into markup contributes nothing, which is the point: an
1273
+ * unauthorized response must be byte-identical to one that never knew about
1274
+ * live preview.
1275
+ */
1276
+ type SuppressedBinding = Readonly<Record<string, never>>;
1277
+ interface PreviewBindingsOptions {
1278
+ /**
1279
+ * The application's own authorization verdict for this request.
1280
+ *
1281
+ * Never derive this from `?preview=true`, an iframe destination or a
1282
+ * referer — those are client-controlled intent signals. Use the same
1283
+ * verified result that decides whether drafts may be read.
1284
+ */
1285
+ readonly authorized: boolean;
1286
+ /**
1287
+ * Document this subtree belongs to, emitted as `data-payload-owner` by
1288
+ * {@link PreviewBindings.owner}. Required only when the page previews more
1289
+ * than one document and the runtime runs with `scopeBindingsByOwner`.
1290
+ */
1291
+ readonly owner?: string;
1292
+ }
1293
+ /** Request-scoped binding helpers that carry one authorization decision. */
1294
+ interface PreviewBindings {
1295
+ /** The verdict these helpers were built with. */
1296
+ readonly authorized: boolean;
1297
+ /** Typed field binding, or nothing at all while unauthorized. */
1298
+ bind: <T = Record<string, unknown>>(field: FieldName<T>, options?: BindOptions) => FieldBindingAttributes | SuppressedBinding;
1299
+ /** Rename-safe field binding, or nothing at all while unauthorized. */
1300
+ bindByPath: <T = Record<string, unknown>>(picker: (data: T) => unknown, options?: BindOptions) => FieldBindingAttributes | SuppressedBinding;
1301
+ /**
1302
+ * Owner marker for the element that owns this subtree, or nothing at all
1303
+ * while unauthorized or when no owner was configured.
1304
+ */
1305
+ owner: () => OwnerBindingAttributes | SuppressedBinding;
1306
+ }
1307
+ /**
1308
+ * Build request-scoped binding helpers around one authorization decision.
1309
+ *
1310
+ * ```ts
1311
+ * const preview = createPreviewBindings({
1312
+ * authorized: authorization !== null,
1313
+ * owner: `global:${slug}`,
1314
+ * });
1315
+ * ```
1316
+ * ```astro
1317
+ * <section {...preview.owner()}>
1318
+ * <h1 {...preview.bind<Homepage>('heroTitle')}>{data.heroTitle}</h1>
1319
+ * </section>
1320
+ * ```
1321
+ *
1322
+ * While unauthorized every helper returns an empty attribute set, so the
1323
+ * public response carries no `data-payload-*` at all.
1324
+ */
1325
+ declare function createPreviewBindings(options: PreviewBindingsOptions): PreviewBindings;
1326
+
1229
1327
  /**
1230
1328
  * Narrow public entry — the minimum API for sites that only need to
1231
1329
  * receive postMessage updates and apply them to the DOM.
@@ -1247,4 +1345,4 @@ declare function bindByPath<T = Record<string, unknown>>(picker: (data: T) => un
1247
1345
 
1248
1346
  declare const CORE_ENTRY = true;
1249
1347
 
1250
- 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 };
1348
+ 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 };
package/dist/core.d.ts CHANGED
@@ -1186,12 +1186,24 @@ interface FieldBindingAttributes {
1186
1186
  readonly 'data-payload-field': string;
1187
1187
  readonly 'data-payload-attribute'?: string;
1188
1188
  readonly 'data-payload-type'?: string;
1189
+ readonly 'data-payload-richtext'?: string;
1190
+ readonly 'data-payload-html'?: string;
1191
+ readonly 'data-payload-locale'?: string;
1189
1192
  }
1190
1193
  interface BindOptions {
1191
1194
  /** Override the bound attribute (e.g., `'src'` for `<img>`). Default: textContent. */
1192
1195
  readonly attribute?: string;
1193
1196
  /** Explicit field-type override — bypasses schema detection. */
1194
1197
  readonly type?: string;
1198
+ /**
1199
+ * Mark the binding as Lexical rich text. Usually detected automatically;
1200
+ * set it where the initial render is empty and there is nothing to detect.
1201
+ */
1202
+ readonly richtext?: boolean;
1203
+ /** Render the value as sanitised HTML rather than text. */
1204
+ readonly html?: boolean;
1205
+ /** Lock this element to one locale, overriding the message locale. */
1206
+ readonly locale?: string;
1195
1207
  }
1196
1208
  /**
1197
1209
  * String-literal binding helper.
@@ -1226,6 +1238,92 @@ declare function bind<T = Record<string, unknown>>(field: FieldName<T>, options?
1226
1238
  */
1227
1239
  declare function bindByPath<T = Record<string, unknown>>(picker: (data: T) => unknown, options?: BindOptions): FieldBindingAttributes;
1228
1240
 
1241
+ /**
1242
+ * Authorization-gated binding emission.
1243
+ *
1244
+ * Binding attributes are not neutral markup. `data-payload-field` names a CMS
1245
+ * field, and `data-payload-owner` names a global, a collection and often a
1246
+ * document id. Emitted unconditionally they publish the shape of the content
1247
+ * model — and the identity of documents — to every anonymous visitor and
1248
+ * crawler of the page.
1249
+ *
1250
+ * The gate belongs to the application, not to this package: it is the same
1251
+ * decision that already controls draft reads and cache policy. What this
1252
+ * module provides is a place to apply that decision **once per request** so no
1253
+ * individual call site can forget it, and an emission unit that cannot be
1254
+ * partially suppressed.
1255
+ *
1256
+ * That second property matters more than it looks. A binding is not one
1257
+ * attribute: a field travels with its type, its locale, its rich-text marker
1258
+ * and its owner. Gating only the field name leaves the companions behind,
1259
+ * which discloses the taxonomy anyway and leaves the runtime looking at a
1260
+ * binding whose field is gone.
1261
+ *
1262
+ * @module @dsl/preview-bindings
1263
+ */
1264
+
1265
+ /** Owner marker naming the document a subtree belongs to. */
1266
+ interface OwnerBindingAttributes {
1267
+ readonly 'data-payload-owner': string;
1268
+ }
1269
+ /**
1270
+ * The complete absence of binding attributes.
1271
+ *
1272
+ * Spreading this into markup contributes nothing, which is the point: an
1273
+ * unauthorized response must be byte-identical to one that never knew about
1274
+ * live preview.
1275
+ */
1276
+ type SuppressedBinding = Readonly<Record<string, never>>;
1277
+ interface PreviewBindingsOptions {
1278
+ /**
1279
+ * The application's own authorization verdict for this request.
1280
+ *
1281
+ * Never derive this from `?preview=true`, an iframe destination or a
1282
+ * referer — those are client-controlled intent signals. Use the same
1283
+ * verified result that decides whether drafts may be read.
1284
+ */
1285
+ readonly authorized: boolean;
1286
+ /**
1287
+ * Document this subtree belongs to, emitted as `data-payload-owner` by
1288
+ * {@link PreviewBindings.owner}. Required only when the page previews more
1289
+ * than one document and the runtime runs with `scopeBindingsByOwner`.
1290
+ */
1291
+ readonly owner?: string;
1292
+ }
1293
+ /** Request-scoped binding helpers that carry one authorization decision. */
1294
+ interface PreviewBindings {
1295
+ /** The verdict these helpers were built with. */
1296
+ readonly authorized: boolean;
1297
+ /** Typed field binding, or nothing at all while unauthorized. */
1298
+ bind: <T = Record<string, unknown>>(field: FieldName<T>, options?: BindOptions) => FieldBindingAttributes | SuppressedBinding;
1299
+ /** Rename-safe field binding, or nothing at all while unauthorized. */
1300
+ bindByPath: <T = Record<string, unknown>>(picker: (data: T) => unknown, options?: BindOptions) => FieldBindingAttributes | SuppressedBinding;
1301
+ /**
1302
+ * Owner marker for the element that owns this subtree, or nothing at all
1303
+ * while unauthorized or when no owner was configured.
1304
+ */
1305
+ owner: () => OwnerBindingAttributes | SuppressedBinding;
1306
+ }
1307
+ /**
1308
+ * Build request-scoped binding helpers around one authorization decision.
1309
+ *
1310
+ * ```ts
1311
+ * const preview = createPreviewBindings({
1312
+ * authorized: authorization !== null,
1313
+ * owner: `global:${slug}`,
1314
+ * });
1315
+ * ```
1316
+ * ```astro
1317
+ * <section {...preview.owner()}>
1318
+ * <h1 {...preview.bind<Homepage>('heroTitle')}>{data.heroTitle}</h1>
1319
+ * </section>
1320
+ * ```
1321
+ *
1322
+ * While unauthorized every helper returns an empty attribute set, so the
1323
+ * public response carries no `data-payload-*` at all.
1324
+ */
1325
+ declare function createPreviewBindings(options: PreviewBindingsOptions): PreviewBindings;
1326
+
1229
1327
  /**
1230
1328
  * Narrow public entry — the minimum API for sites that only need to
1231
1329
  * receive postMessage updates and apply them to the DOM.
@@ -1247,4 +1345,4 @@ declare function bindByPath<T = Record<string, unknown>>(picker: (data: T) => un
1247
1345
 
1248
1346
  declare const CORE_ENTRY = true;
1249
1347
 
1250
- 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 };
1348
+ 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 };