payload-live-preview 2.0.0-beta.0 → 2.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 (109) hide show
  1. package/README.md +60 -775
  2. package/dist/adapters/astro/PreviewBoundary.astro +2 -2
  3. package/dist/adapters/astro/index.d.ts +28 -154
  4. package/dist/adapters/astro/index.js +1 -1
  5. package/dist/adapters/astro/index.js.map +1 -1
  6. package/dist/adapters/astro/middleware-entry.d.ts +3 -2
  7. package/dist/adapters/astro/middleware-entry.js +1 -1
  8. package/dist/adapters/astro/middleware-entry.js.map +1 -1
  9. package/dist/adapters/nextjs/index.d.ts +247 -6
  10. package/dist/adapters/nextjs/index.js +1 -1
  11. package/dist/adapters/nextjs/index.js.map +1 -1
  12. package/dist/adapters/nuxt/index.d.ts +58 -3
  13. package/dist/adapters/nuxt/index.js +1 -1
  14. package/dist/adapters/nuxt/index.js.map +1 -1
  15. package/dist/adapters/nuxt/module.d.ts +82 -0
  16. package/dist/adapters/nuxt/module.js +2 -0
  17. package/dist/adapters/nuxt/module.js.map +1 -0
  18. package/dist/adapters/react/index.d.ts +110 -0
  19. package/dist/adapters/react/index.js +3 -0
  20. package/dist/adapters/react/index.js.map +1 -0
  21. package/dist/adapters/sveltekit/index.d.ts +65 -7
  22. package/dist/adapters/sveltekit/index.js +1 -1
  23. package/dist/adapters/sveltekit/index.js.map +1 -1
  24. package/dist/adapters/vue/index.d.ts +56 -0
  25. package/dist/adapters/vue/index.js +2 -0
  26. package/dist/adapters/vue/index.js.map +1 -0
  27. package/dist/annotate.d.ts +161 -0
  28. package/dist/annotate.js +2 -0
  29. package/dist/annotate.js.map +1 -0
  30. package/dist/client.cjs +1 -1
  31. package/dist/client.cjs.map +1 -1
  32. package/dist/client.d.cts +182 -9
  33. package/dist/client.d.ts +182 -9
  34. package/dist/client.js +1 -1
  35. package/dist/client.js.map +1 -1
  36. package/dist/codegen-astro.js +1 -1
  37. package/dist/codegen-astro.js.map +1 -1
  38. package/dist/codegen-cli.js +1 -1
  39. package/dist/codegen-cli.js.map +1 -1
  40. package/dist/codegen.cjs +1 -1
  41. package/dist/codegen.cjs.map +1 -1
  42. package/dist/codegen.d.cts +129 -4
  43. package/dist/codegen.d.ts +129 -4
  44. package/dist/codegen.js +1 -1
  45. package/dist/codegen.js.map +1 -1
  46. package/dist/core.cjs +1 -1
  47. package/dist/core.cjs.map +1 -1
  48. package/dist/core.d.cts +264 -73
  49. package/dist/core.d.ts +264 -73
  50. package/dist/core.js +1 -1
  51. package/dist/core.js.map +1 -1
  52. package/dist/doctor-cli.d.ts +1 -1
  53. package/dist/doctor-cli.js +1 -1
  54. package/dist/doctor-cli.js.map +1 -1
  55. package/dist/doctor.d.ts +7 -6
  56. package/dist/doctor.js +1 -1
  57. package/dist/doctor.js.map +1 -1
  58. package/dist/document-session-BfBfLVpg.d.ts +53 -0
  59. package/dist/fragment-endpoint-3Mlz7GDz.d.ts +189 -0
  60. package/dist/fragment.cjs +1 -1
  61. package/dist/fragment.cjs.map +1 -1
  62. package/dist/fragment.d.cts +64 -18
  63. package/dist/fragment.d.ts +64 -18
  64. package/dist/fragment.js +1 -1
  65. package/dist/fragment.js.map +1 -1
  66. package/dist/index.cjs +1 -1
  67. package/dist/index.cjs.map +1 -1
  68. package/dist/index.d.cts +422 -83
  69. package/dist/index.d.ts +422 -83
  70. package/dist/index.js +1 -1
  71. package/dist/index.js.map +1 -1
  72. package/dist/lean.cjs +2 -0
  73. package/dist/lean.cjs.map +1 -0
  74. package/dist/lean.d.cts +44 -0
  75. package/dist/lean.d.ts +44 -0
  76. package/dist/lean.js +2 -0
  77. package/dist/lean.js.map +1 -0
  78. package/dist/lexical.cjs +1 -1
  79. package/dist/lexical.cjs.map +1 -1
  80. package/dist/lexical.d.cts +19 -1
  81. package/dist/lexical.d.ts +19 -1
  82. package/dist/lexical.js +1 -1
  83. package/dist/lexical.js.map +1 -1
  84. package/dist/locals-DBXDMxOA.d.ts +45 -0
  85. package/dist/{middleware-CEPsY1P7.d.ts → middleware-Bx9I7PXW.d.ts} +14 -14
  86. package/dist/migrate.d.ts +4 -3
  87. package/dist/migrate.js +1 -1
  88. package/dist/migrate.js.map +1 -1
  89. package/dist/{options-CEzzfHJu.d.ts → options-BW1eqXvc.d.ts} +90 -2
  90. package/dist/plugins.cjs +1 -1
  91. package/dist/plugins.cjs.map +1 -1
  92. package/dist/plugins.d.cts +111 -6
  93. package/dist/plugins.d.ts +111 -6
  94. package/dist/plugins.js +1 -1
  95. package/dist/plugins.js.map +1 -1
  96. package/dist/{probe-CYtvj_Lm.d.ts → probe-CELeUciC.d.ts} +26 -4
  97. package/dist/server.cjs +1 -1
  98. package/dist/server.cjs.map +1 -1
  99. package/dist/server.d.cts +67 -13
  100. package/dist/server.d.ts +67 -13
  101. package/dist/server.js +1 -1
  102. package/dist/server.js.map +1 -1
  103. package/dist/structural.cjs +2 -2
  104. package/dist/structural.cjs.map +1 -1
  105. package/dist/structural.d.cts +47 -4
  106. package/dist/structural.d.ts +47 -4
  107. package/dist/structural.js +2 -2
  108. package/dist/structural.js.map +1 -1
  109. package/package.json +77 -9
package/dist/server.d.cts CHANGED
@@ -141,6 +141,22 @@ type PreviewTokenTransport = {
141
141
  * a load balancer, so the store is a deployment decision.
142
142
  */
143
143
  interface PreviewTokenReplayStore {
144
+ /**
145
+ * Record `id` as used until `expiresAt` and say whether this call was the
146
+ * first: `true` admits the token, anything else refuses it as `replayed`, a
147
+ * throw refuses it as `unavailable`. Check and record in one step (Redis
148
+ * `SET NX PX`, a unique insert): a read followed by a write lets two requests
149
+ * that carry the same token and arrive together both pass.
150
+ */
151
+ consume(id: string, expiresAt: number): Promise<boolean> | boolean;
152
+ }
153
+ /**
154
+ * @deprecated The 1.x store shape, removed in 3.0. Its two calls cannot be one
155
+ * step, so two requests with the same token that arrive together both pass
156
+ * `isUsed` before either reaches `markUsed`. Implement
157
+ * {@link PreviewTokenReplayStore.consume} instead.
158
+ */
159
+ interface PreviewTokenReplayChecks {
144
160
  isUsed(id: string): Promise<boolean> | boolean;
145
161
  markUsed(id: string, expiresAt: number): Promise<void> | void;
146
162
  }
@@ -156,7 +172,7 @@ interface SignedTokenStrategy {
156
172
  readonly transport?: PreviewTokenTransport;
157
173
  /** Resolve the request's locale for the `loc` binding. Unset: tokens with `loc` are refused as `wrong-locale`. */
158
174
  readonly locale?: (request: PreviewAuthorizationRequest) => string | undefined;
159
- readonly replay?: PreviewTokenReplayStore;
175
+ readonly replay?: PreviewTokenReplayStore | PreviewTokenReplayChecks;
160
176
  readonly crypto?: SubtleCryptoLike;
161
177
  readonly now?: () => number;
162
178
  }
@@ -216,7 +232,9 @@ interface PreviewRequestOptions {
216
232
  readonly signals?: readonly PreviewSignal[];
217
233
  /** Query parameters (value `true` or `1`) that signal intent. Default `['preview', 'draft', 'livePreview']`. */
218
234
  readonly queryParams?: readonly string[];
219
- /** Admin origins whose Referer signals intent, e.g. `['https://cms.example.com']`. */
235
+ /** Admin origins whose Referer signals intent, e.g. `['https://cms.example.com']` — the adapters' option of the same name. */
236
+ readonly allowedOrigins?: readonly string[];
237
+ /** @deprecated Use `allowedOrigins`; removed in 3.0. Ignored when `allowedOrigins` is given. */
220
238
  readonly adminOrigins?: readonly string[];
221
239
  }
222
240
  /** The request surface the predicate reads; `Request` satisfies it, Nitro/H3 events need a shim. */
@@ -232,12 +250,12 @@ declare function hasPreviewIntent(request: PreviewRequestLike, options?: Preview
232
250
  /** Type-level field paths, so a binding name is checked at compile time against the codegen schema. */
233
251
  /** String keys of `T`. */
234
252
  type FieldName<T> = Extract<keyof T, string>;
235
- /** Dotted paths into `T`, capped at three levels so IntelliSense stays usable. */
253
+ /** Dotted paths into `T`, capped at three levels so IntelliSense stays usable. @internal */
236
254
  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 ? {
237
255
  [K in Extract<keyof T, string>]: K | (T[K] extends object ? `${K}.${FieldPath<T[K], Prev<Depth>>}` : never);
238
256
  }[Extract<keyof T, string>] : never;
239
257
  type Prev<N extends 0 | 1 | 2 | 3> = N extends 3 ? 2 : N extends 2 ? 1 : N extends 1 ? 0 : 0;
240
- /** The value type at a dotted path, or `unknown` when the path does not exist. */
258
+ /** The value type at a dotted path, or `unknown` when the path does not exist. @internal */
241
259
  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;
242
260
 
243
261
  /**
@@ -289,15 +307,25 @@ declare function bindByPath<T = Record<string, unknown>>(picker: (data: T) => un
289
307
  interface OwnerBindingAttributes {
290
308
  readonly 'data-payload-owner': string;
291
309
  }
310
+ /** The attributes marking a server-rendered boundary (ADR 0011, docs/hybrid.md). */
311
+ interface FragmentBoundaryAttributes {
312
+ readonly 'data-payload-fragment': string;
313
+ readonly 'data-payload-fragment-key'?: string;
314
+ readonly 'data-payload-depends'?: string;
315
+ }
316
+ interface FragmentBoundaryOptions {
317
+ /** Distinguishes boundaries when one registry id renders several on a page. */
318
+ readonly key?: string;
319
+ /** The fields that re-render the boundary. Without it, every update does. */
320
+ readonly dependsOn?: readonly string[];
321
+ }
292
322
  /** No attributes at all: an unauthorized response is byte-identical to one that never knew about live preview. */
293
323
  type SuppressedBinding = Readonly<Record<string, never>>;
294
- interface PreviewBindingsCommonOptions {
295
- /** Document this subtree belongs to, emitted as `data-payload-owner`; needed with `scopeBindingsByOwner`. */
296
- readonly owner?: string;
297
- }
298
324
  /** The verdict from `authorizePreviewRequest()`, or `null` for a public response. Only a branded context authorizes emission. */
299
- interface PreviewBindingsOptions extends PreviewBindingsCommonOptions {
325
+ interface PreviewBindingsOptions {
300
326
  readonly authorization: AuthorizedPreviewContext | null;
327
+ /** Document this subtree belongs to, emitted as `data-payload-owner`; needed with `scopeBindingsByOwner`. */
328
+ readonly owner?: string;
301
329
  }
302
330
  /** Request-scoped binding helpers carrying one authorization decision. */
303
331
  interface PreviewBindings {
@@ -308,14 +336,40 @@ interface PreviewBindings {
308
336
  bindByPath: <T = Record<string, unknown>>(picker: (data: T) => unknown, options?: BindOptions) => FieldBindingAttributes | SuppressedBinding;
309
337
  /** Owner marker for this subtree, or nothing while unauthorized or without an owner. */
310
338
  owner: () => OwnerBindingAttributes | SuppressedBinding;
339
+ /**
340
+ * A server-rendered boundary, or nothing while unauthorized — the id and the
341
+ * fields it depends on describe the content model as much as a binding does.
342
+ * The registry behind `id` is the endpoint's (docs/hybrid.md).
343
+ */
344
+ boundary: (id: string, options?: FragmentBoundaryOptions) => FragmentBoundaryAttributes | SuppressedBinding;
311
345
  }
312
346
  /** Request-scoped `bind`, `bindByPath` and `owner`, all suppressed unless `authorization` is a real context. */
313
347
  declare function createPreviewBindings(options: PreviewBindingsOptions): PreviewBindings;
314
348
 
349
+ /** What a decision publishes for templates on `locals` (Astro, SvelteKit) or `event.context` (Nuxt). */
350
+
351
+ /**
352
+ * The binding helpers for one request, read straight off whatever the framework
353
+ * calls locals — `Astro.locals`, SvelteKit's `event.locals`, Nuxt's
354
+ * `event.context`.
355
+ *
356
+ * `createPreviewBindings({ authorization: locals.livePreviewAuthorization })`
357
+ * says the same thing, and a template that spells it out is welcome to. This
358
+ * exists because generated code should not have to: the build-time annotator
359
+ * writes one call per component, and a helper that takes `unknown` needs no
360
+ * `App.Locals` declaration in the project it writes into. The verdict is
361
+ * checked, not trusted — only a context `authorizePreviewRequest()` produced
362
+ * emits anything (ADR 0006).
363
+ * @internal
364
+ */
365
+ declare function previewBindingsFromLocals(locals: unknown, options?: {
366
+ readonly owner?: string;
367
+ }): PreviewBindings;
368
+
315
369
  /**
316
- * `payload-live-preview/server` — the privileged surface: the initial draft
317
- * read, token issuance and verification, and the gated binding helpers. The
318
- * architecture policy keeps it out of every browser bundle.
370
+ * `definePreview()`: origin, route and depth bound once, so the initial draft
371
+ * read and the runtime's `mergeDepth` cannot drift apart. Every read takes an
372
+ * explicit authorization verdict; only a real context reads the draft.
319
373
  */
320
374
 
321
375
  /**
@@ -432,4 +486,4 @@ interface PreviewServer {
432
486
  /** Bind origin, route and depth once; read documents and globals with an explicit authorization verdict. */
433
487
  declare function definePreview(config: PreviewServerConfig): PreviewServer;
434
488
 
435
- export { AUTHORIZED_PREVIEW_BRAND_KEY, type AuthorizedPreviewContext, type AuthorizedPreviewScope, type BindOptions, type FetchLike, type FieldBindingAttributes, type FieldName, type FieldPath, type IssuePreviewTokenOptions, type OwnerBindingAttributes, type PayloadSessionStrategy, type PreviewAuthorization, type PreviewAuthorizationOutcome, type PreviewAuthorizationRequest, type PreviewAuthorizationStrategy, type PreviewAuthorizationStrategyName, type PreviewBindings, type PreviewBindingsCommonOptions, type PreviewBindingsOptions, type PreviewFetchDiagnostic, PreviewFetchError, type PreviewFetchFailureReason, type PreviewFetchFunction, type PreviewFetchResult, type PreviewReadOptions, type PreviewRequestLike, type PreviewRequestOptions, type PreviewRuntimeOptions, type PreviewServer, type PreviewServerConfig, type PreviewSignal, type PreviewTokenClaims, type PreviewTokenReplayStore, type PreviewTokenTransport, type PreviewVerifierClaims, type PreviewWhere, type PreviewWhereValue, type ReadDocumentOptions, type ReadGlobalOptions, type SignedTokenStrategy, type SubtleCryptoLike, type SuppressedBinding, type ValueAt, type VerifierStrategy, authorizePreviewRequest, bind, bindByPath, createPreviewBindings, definePreview, extractCookie, hasPreviewIntent, isAuthorizedPreviewContext, issuePreviewToken };
489
+ export { AUTHORIZED_PREVIEW_BRAND_KEY, type AuthorizedPreviewContext, type AuthorizedPreviewScope, type BindOptions, type FetchLike, type FieldBindingAttributes, type FieldName, type FieldPath, type FragmentBoundaryAttributes, type FragmentBoundaryOptions, type IssuePreviewTokenOptions, type OwnerBindingAttributes, type PayloadSessionStrategy, type PreviewAuthorization, type PreviewAuthorizationOutcome, type PreviewAuthorizationRequest, type PreviewAuthorizationStrategy, type PreviewAuthorizationStrategyName, type PreviewBindings, type PreviewBindingsOptions, type PreviewFetchDiagnostic, PreviewFetchError, type PreviewFetchFailureReason, type PreviewFetchFunction, type PreviewFetchResult, type PreviewReadOptions, type PreviewRequestLike, type PreviewRequestOptions, type PreviewRuntimeOptions, type PreviewServer, type PreviewServerConfig, type PreviewSignal, type PreviewTokenClaims, type PreviewTokenReplayChecks, type PreviewTokenReplayStore, type PreviewTokenTransport, type PreviewVerifierClaims, type PreviewWhere, type PreviewWhereValue, type ReadDocumentOptions, type ReadGlobalOptions, type SignedTokenStrategy, type SubtleCryptoLike, type SuppressedBinding, type ValueAt, type VerifierStrategy, authorizePreviewRequest, bind, bindByPath, createPreviewBindings, definePreview, extractCookie, hasPreviewIntent, isAuthorizedPreviewContext, issuePreviewToken, previewBindingsFromLocals };
package/dist/server.d.ts CHANGED
@@ -141,6 +141,22 @@ type PreviewTokenTransport = {
141
141
  * a load balancer, so the store is a deployment decision.
142
142
  */
143
143
  interface PreviewTokenReplayStore {
144
+ /**
145
+ * Record `id` as used until `expiresAt` and say whether this call was the
146
+ * first: `true` admits the token, anything else refuses it as `replayed`, a
147
+ * throw refuses it as `unavailable`. Check and record in one step (Redis
148
+ * `SET NX PX`, a unique insert): a read followed by a write lets two requests
149
+ * that carry the same token and arrive together both pass.
150
+ */
151
+ consume(id: string, expiresAt: number): Promise<boolean> | boolean;
152
+ }
153
+ /**
154
+ * @deprecated The 1.x store shape, removed in 3.0. Its two calls cannot be one
155
+ * step, so two requests with the same token that arrive together both pass
156
+ * `isUsed` before either reaches `markUsed`. Implement
157
+ * {@link PreviewTokenReplayStore.consume} instead.
158
+ */
159
+ interface PreviewTokenReplayChecks {
144
160
  isUsed(id: string): Promise<boolean> | boolean;
145
161
  markUsed(id: string, expiresAt: number): Promise<void> | void;
146
162
  }
@@ -156,7 +172,7 @@ interface SignedTokenStrategy {
156
172
  readonly transport?: PreviewTokenTransport;
157
173
  /** Resolve the request's locale for the `loc` binding. Unset: tokens with `loc` are refused as `wrong-locale`. */
158
174
  readonly locale?: (request: PreviewAuthorizationRequest) => string | undefined;
159
- readonly replay?: PreviewTokenReplayStore;
175
+ readonly replay?: PreviewTokenReplayStore | PreviewTokenReplayChecks;
160
176
  readonly crypto?: SubtleCryptoLike;
161
177
  readonly now?: () => number;
162
178
  }
@@ -216,7 +232,9 @@ interface PreviewRequestOptions {
216
232
  readonly signals?: readonly PreviewSignal[];
217
233
  /** Query parameters (value `true` or `1`) that signal intent. Default `['preview', 'draft', 'livePreview']`. */
218
234
  readonly queryParams?: readonly string[];
219
- /** Admin origins whose Referer signals intent, e.g. `['https://cms.example.com']`. */
235
+ /** Admin origins whose Referer signals intent, e.g. `['https://cms.example.com']` — the adapters' option of the same name. */
236
+ readonly allowedOrigins?: readonly string[];
237
+ /** @deprecated Use `allowedOrigins`; removed in 3.0. Ignored when `allowedOrigins` is given. */
220
238
  readonly adminOrigins?: readonly string[];
221
239
  }
222
240
  /** The request surface the predicate reads; `Request` satisfies it, Nitro/H3 events need a shim. */
@@ -232,12 +250,12 @@ declare function hasPreviewIntent(request: PreviewRequestLike, options?: Preview
232
250
  /** Type-level field paths, so a binding name is checked at compile time against the codegen schema. */
233
251
  /** String keys of `T`. */
234
252
  type FieldName<T> = Extract<keyof T, string>;
235
- /** Dotted paths into `T`, capped at three levels so IntelliSense stays usable. */
253
+ /** Dotted paths into `T`, capped at three levels so IntelliSense stays usable. @internal */
236
254
  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 ? {
237
255
  [K in Extract<keyof T, string>]: K | (T[K] extends object ? `${K}.${FieldPath<T[K], Prev<Depth>>}` : never);
238
256
  }[Extract<keyof T, string>] : never;
239
257
  type Prev<N extends 0 | 1 | 2 | 3> = N extends 3 ? 2 : N extends 2 ? 1 : N extends 1 ? 0 : 0;
240
- /** The value type at a dotted path, or `unknown` when the path does not exist. */
258
+ /** The value type at a dotted path, or `unknown` when the path does not exist. @internal */
241
259
  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;
242
260
 
243
261
  /**
@@ -289,15 +307,25 @@ declare function bindByPath<T = Record<string, unknown>>(picker: (data: T) => un
289
307
  interface OwnerBindingAttributes {
290
308
  readonly 'data-payload-owner': string;
291
309
  }
310
+ /** The attributes marking a server-rendered boundary (ADR 0011, docs/hybrid.md). */
311
+ interface FragmentBoundaryAttributes {
312
+ readonly 'data-payload-fragment': string;
313
+ readonly 'data-payload-fragment-key'?: string;
314
+ readonly 'data-payload-depends'?: string;
315
+ }
316
+ interface FragmentBoundaryOptions {
317
+ /** Distinguishes boundaries when one registry id renders several on a page. */
318
+ readonly key?: string;
319
+ /** The fields that re-render the boundary. Without it, every update does. */
320
+ readonly dependsOn?: readonly string[];
321
+ }
292
322
  /** No attributes at all: an unauthorized response is byte-identical to one that never knew about live preview. */
293
323
  type SuppressedBinding = Readonly<Record<string, never>>;
294
- interface PreviewBindingsCommonOptions {
295
- /** Document this subtree belongs to, emitted as `data-payload-owner`; needed with `scopeBindingsByOwner`. */
296
- readonly owner?: string;
297
- }
298
324
  /** The verdict from `authorizePreviewRequest()`, or `null` for a public response. Only a branded context authorizes emission. */
299
- interface PreviewBindingsOptions extends PreviewBindingsCommonOptions {
325
+ interface PreviewBindingsOptions {
300
326
  readonly authorization: AuthorizedPreviewContext | null;
327
+ /** Document this subtree belongs to, emitted as `data-payload-owner`; needed with `scopeBindingsByOwner`. */
328
+ readonly owner?: string;
301
329
  }
302
330
  /** Request-scoped binding helpers carrying one authorization decision. */
303
331
  interface PreviewBindings {
@@ -308,14 +336,40 @@ interface PreviewBindings {
308
336
  bindByPath: <T = Record<string, unknown>>(picker: (data: T) => unknown, options?: BindOptions) => FieldBindingAttributes | SuppressedBinding;
309
337
  /** Owner marker for this subtree, or nothing while unauthorized or without an owner. */
310
338
  owner: () => OwnerBindingAttributes | SuppressedBinding;
339
+ /**
340
+ * A server-rendered boundary, or nothing while unauthorized — the id and the
341
+ * fields it depends on describe the content model as much as a binding does.
342
+ * The registry behind `id` is the endpoint's (docs/hybrid.md).
343
+ */
344
+ boundary: (id: string, options?: FragmentBoundaryOptions) => FragmentBoundaryAttributes | SuppressedBinding;
311
345
  }
312
346
  /** Request-scoped `bind`, `bindByPath` and `owner`, all suppressed unless `authorization` is a real context. */
313
347
  declare function createPreviewBindings(options: PreviewBindingsOptions): PreviewBindings;
314
348
 
349
+ /** What a decision publishes for templates on `locals` (Astro, SvelteKit) or `event.context` (Nuxt). */
350
+
351
+ /**
352
+ * The binding helpers for one request, read straight off whatever the framework
353
+ * calls locals — `Astro.locals`, SvelteKit's `event.locals`, Nuxt's
354
+ * `event.context`.
355
+ *
356
+ * `createPreviewBindings({ authorization: locals.livePreviewAuthorization })`
357
+ * says the same thing, and a template that spells it out is welcome to. This
358
+ * exists because generated code should not have to: the build-time annotator
359
+ * writes one call per component, and a helper that takes `unknown` needs no
360
+ * `App.Locals` declaration in the project it writes into. The verdict is
361
+ * checked, not trusted — only a context `authorizePreviewRequest()` produced
362
+ * emits anything (ADR 0006).
363
+ * @internal
364
+ */
365
+ declare function previewBindingsFromLocals(locals: unknown, options?: {
366
+ readonly owner?: string;
367
+ }): PreviewBindings;
368
+
315
369
  /**
316
- * `payload-live-preview/server` — the privileged surface: the initial draft
317
- * read, token issuance and verification, and the gated binding helpers. The
318
- * architecture policy keeps it out of every browser bundle.
370
+ * `definePreview()`: origin, route and depth bound once, so the initial draft
371
+ * read and the runtime's `mergeDepth` cannot drift apart. Every read takes an
372
+ * explicit authorization verdict; only a real context reads the draft.
319
373
  */
320
374
 
321
375
  /**
@@ -432,4 +486,4 @@ interface PreviewServer {
432
486
  /** Bind origin, route and depth once; read documents and globals with an explicit authorization verdict. */
433
487
  declare function definePreview(config: PreviewServerConfig): PreviewServer;
434
488
 
435
- export { AUTHORIZED_PREVIEW_BRAND_KEY, type AuthorizedPreviewContext, type AuthorizedPreviewScope, type BindOptions, type FetchLike, type FieldBindingAttributes, type FieldName, type FieldPath, type IssuePreviewTokenOptions, type OwnerBindingAttributes, type PayloadSessionStrategy, type PreviewAuthorization, type PreviewAuthorizationOutcome, type PreviewAuthorizationRequest, type PreviewAuthorizationStrategy, type PreviewAuthorizationStrategyName, type PreviewBindings, type PreviewBindingsCommonOptions, type PreviewBindingsOptions, type PreviewFetchDiagnostic, PreviewFetchError, type PreviewFetchFailureReason, type PreviewFetchFunction, type PreviewFetchResult, type PreviewReadOptions, type PreviewRequestLike, type PreviewRequestOptions, type PreviewRuntimeOptions, type PreviewServer, type PreviewServerConfig, type PreviewSignal, type PreviewTokenClaims, type PreviewTokenReplayStore, type PreviewTokenTransport, type PreviewVerifierClaims, type PreviewWhere, type PreviewWhereValue, type ReadDocumentOptions, type ReadGlobalOptions, type SignedTokenStrategy, type SubtleCryptoLike, type SuppressedBinding, type ValueAt, type VerifierStrategy, authorizePreviewRequest, bind, bindByPath, createPreviewBindings, definePreview, extractCookie, hasPreviewIntent, isAuthorizedPreviewContext, issuePreviewToken };
489
+ export { AUTHORIZED_PREVIEW_BRAND_KEY, type AuthorizedPreviewContext, type AuthorizedPreviewScope, type BindOptions, type FetchLike, type FieldBindingAttributes, type FieldName, type FieldPath, type FragmentBoundaryAttributes, type FragmentBoundaryOptions, type IssuePreviewTokenOptions, type OwnerBindingAttributes, type PayloadSessionStrategy, type PreviewAuthorization, type PreviewAuthorizationOutcome, type PreviewAuthorizationRequest, type PreviewAuthorizationStrategy, type PreviewAuthorizationStrategyName, type PreviewBindings, type PreviewBindingsOptions, type PreviewFetchDiagnostic, PreviewFetchError, type PreviewFetchFailureReason, type PreviewFetchFunction, type PreviewFetchResult, type PreviewReadOptions, type PreviewRequestLike, type PreviewRequestOptions, type PreviewRuntimeOptions, type PreviewServer, type PreviewServerConfig, type PreviewSignal, type PreviewTokenClaims, type PreviewTokenReplayChecks, type PreviewTokenReplayStore, type PreviewTokenTransport, type PreviewVerifierClaims, type PreviewWhere, type PreviewWhereValue, type ReadDocumentOptions, type ReadGlobalOptions, type SignedTokenStrategy, type SubtleCryptoLike, type SuppressedBinding, type ValueAt, type VerifierStrategy, authorizePreviewRequest, bind, bindByPath, createPreviewBindings, definePreview, extractCookie, hasPreviewIntent, isAuthorizedPreviewContext, issuePreviewToken, previewBindingsFromLocals };
package/dist/server.js CHANGED
@@ -1,2 +1,2 @@
1
- var e="payload-live-preview.authorized-preview-context",t=Symbol.for(e);function isAuthorizedPreviewContext(e){return"object"==typeof e&&null!==e&&!0===e[t]&&Object.isFrozen(e)}function r(r){return Object.freeze({...r,scope:Object.freeze({...r.scope}),payloadHeaders:Object.freeze({...r.payloadHeaders}),[t]:!0,[e]:!0})}var n=class extends Error{name="PreviewConfigurationError"};function o(e){return{authorized:!1,outcome:e,context:null}}var a="users",i=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/,s=/^[A-Za-z0-9._~-]+$/;function c(e,t,r=4096){if(null===e||0===e.length)return null;const n=`${t}=`,o=[];for(const t of e.split(";")){const e=t.trim();e.startsWith(n)&&o.push(e.slice(n.length))}if(1!==o.length)return null;const a=o[0]??"";return 0===a.length||a.length>r||!s.test(a)?null:a}var u,l="live-preview",d="v1";function p(){return u??=new TextEncoder,u}function f(e){const t="string"==typeof e?p().encode(e):e;if(t.byteLength<32)throw new n(`payload-live-preview: the preview token secret must be at least ${String(32)} bytes`);return t}function h(e){if(void 0!==e)return e;const t=globalThis.crypto;return"object"==typeof t?.subtle?t:void 0}async function y(e,t){return e.subtle.importKey("raw",t,{name:"HMAC",hash:"SHA-256"},!1,["sign","verify"])}function v(e){let t="";for(const r of e)t+=String.fromCharCode(r);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function w(e){if(!/^[A-Za-z0-9_-]*$/.test(e))return;const t=e.replace(/-/g,"+").replace(/_/g,"/")+"=".repeat((4-e.length%4)%4);try{const e=atob(t),r=new Uint8Array(e.length);for(let t=0;t<e.length;t+=1)r[t]=e.charCodeAt(t);return r}catch{return}}async function g(e,t){const r=h(t.crypto);if(void 0===r)throw new Error("payload-live-preview: no Web Crypto implementation available to sign tokens");const n=f(t.secret),o=(t.now??Date.now)(),a=Math.min(36e5,Math.max(1,e.ttlMs??6e5)),i=new Uint8Array(16);r.getRandomValues(i);const s={v:1,aud:e.audience,...void 0!==e.path?{pth:e.path}:{},...void 0!==e.locale?{loc:e.locale}:{},...void 0!==e.subject?{sub:e.subject}:{},pur:e.purpose??l,iat:o,exp:o+a,jti:v(i)},c=v(p().encode(JSON.stringify(s))),u=`${d}.${c}`,w=await y(r,n);return`${u}.${v(new Uint8Array(await r.subtle.sign("HMAC",w,p().encode(u))))}`}async function b(e,t){switch(t.type){case"payload-session":return async function(e,t){const s=function(e){const t=e??"payload-token";if(!i.test(t))throw new n(`payload-live-preview: cookie name "${t}" is not a valid cookie-name token`);return t}(t.cookieName),u=function(e){let t;try{t=new URL(e)}catch{throw new n(`payload-live-preview: serverURL "${e}" is not an absolute URL`)}if("https:"!==t.protocol&&"http:"!==t.protocol)throw new n(`payload-live-preview: serverURL must be http(s), got "${t.protocol}"`);return t.origin}(t.serverURL),l=c(e.headers.get("cookie"),s,t.maxCookieLength??4096);if(null===l)return o("missing-credential");const d=t.now??Date.now,p=Math.max(250,t.timeoutMs??3e3),f=t.fetch??globalThis.fetch,h=encodeURIComponent(t.usersSlug??a),y=`${s}=${l}`;let v;try{const e=await f(`${u}/api/${h}/me?depth=0`,{headers:{cookie:y,accept:"application/json"},signal:AbortSignal.timeout(p)});if(!e.ok)return o(401===e.status?"invalid":"unavailable");v=await e.json()}catch{return o("unavailable")}const w=function(e,t){if("object"!=typeof e||null===e||Array.isArray(e))return null;const r=e.user;if("object"!=typeof r||null===r||Array.isArray(r))return null;const n=e.collection??r.collection;if(void 0!==n&&n!==t)return null;const o=r.id;return"string"==typeof o&&o.length>0?o:"number"==typeof o&&Number.isFinite(o)?String(o):null}(v,t.usersSlug??a);if(null===w)return o("invalid");const g=function(e){const t=e.exp;return"number"!=typeof t||!Number.isFinite(t)||t<=0?void 0:t<1e11?1e3*t:t}(v);return void 0!==g&&g<=d()?o("expired"):{authorized:!0,outcome:"authorized",context:r({strategy:"payload-session",subject:w,authorizedAt:d(),expiresAt:g,scope:{},payloadHeaders:{cookie:y}})}}(e,t);case"signed-token":return async function(e,t){const n=f(t.secret),a=h(t.crypto);if(void 0===a)return o("unavailable");const i=function(e,t){if("header"===t?.kind)return e.headers.get(t.name??"x-preview-token");let r;try{r=new URL(e.url)}catch{return null}const n=r.searchParams.getAll(t?.param??"previewToken");return 1===n.length?n[0]??null:null}(e,t.transport);if(null===i||0===i.length)return o("missing-credential");if(i.length>4096)return o("invalid");const s=i.split(".");if(3!==s.length||s[0]!==d)return o("invalid");const c=s[1]??"",u=w(c),v=w(s[2]??"");if(void 0===u||32!==v?.byteLength)return o("invalid");let g;try{const e=await y(a,n);g=await a.subtle.verify("HMAC",e,v,p().encode(`${d}.${c}`))}catch{return o("unavailable")}if(!g)return o("invalid");const b=function(e){let t;try{t=JSON.parse((new TextDecoder).decode(e))}catch{return}if("object"!=typeof t||null===t)return;const r=t,n=e=>void 0===r[e]||"string"==typeof r[e];return 1===r.v&&"string"==typeof r.aud&&"string"==typeof r.pur&&"number"==typeof r.iat&&"number"==typeof r.exp&&"string"==typeof r.jti&&n("pth")&&n("loc")&&n("sub")?r:void 0}(u);if(void 0===b)return o("invalid");const m=(t.now??Date.now)();if(b.exp<=m||b.iat>b.exp)return o("expired");if(b.aud!==t.audience)return o("wrong-audience");if(b.pur!==(t.purpose??l))return o("wrong-purpose");if(void 0!==b.pth){let t;try{t=new URL(e.url).pathname}catch{return o("invalid")}if(b.pth!==t)return o("wrong-path")}if(void 0!==b.loc){const r=t.locale?.(e);if(r!==b.loc)return o("wrong-locale")}if(void 0!==t.replay)try{if(await t.replay.isUsed(b.jti))return o("replayed");await t.replay.markUsed(b.jti,b.exp)}catch{return o("unavailable")}return{authorized:!0,outcome:"authorized",context:r({strategy:"signed-token",subject:b.sub,authorizedAt:m,expiresAt:b.exp,scope:{audience:b.aud,...void 0!==b.pth?{path:b.pth}:{},...void 0!==b.loc?{locale:b.loc}:{}},payloadHeaders:{}})}}(e,t);case"verifier":return async function(e,t){let n;try{n=await t.verify(e)}catch{return o("unavailable")}if(null===n)return o("invalid");const a=(t.now??Date.now)();return void 0!==n.expiresAt&&n.expiresAt<=a?o("expired"):{authorized:!0,outcome:"authorized",context:r({strategy:"verifier",subject:n.subject,authorizedAt:a,expiresAt:n.expiresAt,scope:n.scope??{},payloadHeaders:n.payloadHeaders??{}})}}(e,t)}}var m=["preview","draft","livePreview"];function $(e,t={}){const r=new Set(t.signals??["query","fetch-dest","referer"]),n=t.queryParams??m;if(r.has("query")){let t;try{t=new URL(e.url)}catch{t=void 0}if(void 0!==t)for(const e of n){const r=t.searchParams.get(e);if("true"===r||"1"===r)return!0}}if(r.has("fetch-dest")&&"iframe"===e.headers.get("sec-fetch-dest"))return!0;const o=r.has("referer")?t.adminOrigins??[]:[];if(o.length>0){const t=e.headers.get("referer");if(null!==t)try{const e=new URL(t).origin;for(const t of o)try{if(new URL(t).origin===e)return!0}catch{}}catch{}}return!1}function bind(e,t){return A(e,t)}function bindByPath(e,t){const r=function(e){const t=[],r=new Proxy({},{get(e,n){if("symbol"!=typeof n)return/^\d+$/.test(n)||t.push(n),r}});try{e(r)}catch{}return t}(e);if(0===r.length)throw new Error("bindByPath: the picker did not read any field — return data.<field> instead of a constant");return A(r.join("."),t)}function A(e,t){if(0===e.length)throw new Error("bind: field name must be a non-empty string");const r={"data-payload-field":e};return void 0!==t?.attribute&&(r["data-payload-attribute"]=t.attribute),void 0!==t?.type&&(r["data-payload-type"]=t.type),!0===t?.richtext&&(r["data-payload-richtext"]=""),!0===t?.html&&(r["data-payload-html"]=""),void 0!==t?.locale&&(r["data-payload-locale"]=t.locale),void 0!==t?.alt&&(r["data-payload-alt"]=t.alt),void 0!==t?.href&&(r["data-payload-href"]=t.href),void 0!==t?.arrayTemplate&&(r["data-payload-array-template"]=t.arrayTemplate),r}var x=Object.freeze({});function createPreviewBindings(e){const t=isAuthorizedPreviewContext(e.authorization),r=void 0!==e.owner&&e.owner.length>0?e.owner:void 0;return Object.freeze({authorized:t,bind:(e,r)=>t?bind(e,r):x,bindByPath:(e,r)=>t?bindByPath(e,r):x,owner:()=>t&&void 0!==r?{"data-payload-owner":r}:x})}var j=class extends Error{reason;status;url;constructor(e,t,r,n){super(`payload-live-preview: preview read failed (${e}${void 0===r?"":` ${String(r)}`}) for ${t}`,{cause:n}),this.name="PreviewFetchError",this.reason=e,this.status=r,this.url=t}};function z(e){const t=function(e){let t;try{t=new URL(e)}catch{throw new Error(`payload-live-preview: serverURL "${e}" is not an absolute URL`)}if("https:"!==t.protocol&&"http:"!==t.protocol)throw new Error(`payload-live-preview: serverURL must be http(s), got "${t.protocol}"`);return`${t.origin}${t.pathname.replace(/\/+$/,"")}`}(e.serverURL),r=e.apiRoute??"/api",n=r.startsWith("/")?r:`/${r}`;if(!Number.isInteger(e.depth)||e.depth<0)throw new Error(`payload-live-preview: depth must be a non-negative integer, got ${String(e.depth)}`);const o=Math.max(250,e.timeoutMs??5e3),a=Object.freeze({serverURL:t,apiRoute:n,depth:e.depth,timeoutMs:o}),i=Object.freeze({serverURL:t,apiRoute:n,mergeDepth:e.depth}),s=`${t}${n}`;async function c(t,r,n){const a=e.fetch??("function"==typeof fetch?fetch:void 0),i=Date.now(),s=o=>{const a=Date.now()-i;if(o.ok)return e.onDiagnostic?.({kind:"response",url:t,status:o.status,draft:n,durationMs:a}),o;if(e.onDiagnostic?.({kind:"failure",url:t,reason:o.reason,status:o.status,draft:n,durationMs:a}),"throw"===r.errorMode)throw new j(o.reason,t,o.status,o.cause);return o};if(void 0===a)return s({ok:!1,reason:"no-fetch",status:void 0,draft:n,cause:void 0});const c=AbortSignal.timeout(o),u=void 0===r.signal?c:AbortSignal.any([c,r.signal]),l=isAuthorizedPreviewContext(r.authorization)?r.authorization:null;let d,p;try{d=await a(t,{headers:{accept:"application/json",...r.headers??{},...l?.payloadHeaders??{}},signal:u,cache:"no-store",redirect:"error"})}catch(e){return s({ok:!1,reason:k(e,r.signal),status:void 0,draft:n,cause:e})}if(!d.ok)return s({ok:!1,reason:"http",status:d.status,draft:n,cause:void 0});try{p=await d.json()}catch(e){return s({ok:!1,reason:"invalid-json",status:d.status,draft:n,cause:e})}return s({ok:!0,data:p,draft:n,status:d.status})}function u(e){return isAuthorizedPreviewContext(e.authorization)}function l(t,r){const n=new URLSearchParams;return n.set("depth",String(e.depth)),r&&n.set("draft","true"),void 0!==t.locale&&n.set("locale",t.locale),n}return Object.freeze({config:a,runtimeOptions:i,async fetchDocument(e){const t=u(e),r=l(e,t);r.set("limit","1"),void 0!==e.where&&S(r,e.where,["where"]);const n=`${s}/${encodeURIComponent(e.collection)}?${r.toString()}`,o=await c(n,e,t);if(!o.ok)return o;const a=o.data?.docs?.[0];return{ok:!0,data:a??null,draft:t,status:o.status}},async fetchGlobal(e){const t=u(e);return c(`${s}/globals/${encodeURIComponent(e.global)}?${l(e,t).toString()}`,e,t)}})}function k(e,t){if(!0===t?.aborted)return"aborted";const r="object"==typeof e&&null!==e?e.name:void 0;return"TimeoutError"===r?"timeout":"AbortError"===r?"aborted":"network"}function U(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function R(e){return e.map((e,t)=>0===t?e:`[${e}]`).join("")}function S(e,t,r){for(const[n,o]of Object.entries(t)){const t=[...r,n];U(o)?S(e,o,t):Array.isArray(o)&&o.some(U)?o.forEach((r,n)=>{S(e,r,[...t,String(n)])}):Array.isArray(o)?e.set(R(t),o.map(String).join(",")):e.set(R(t),"object"==typeof o?"null":String(o))}}export{e as AUTHORIZED_PREVIEW_BRAND_KEY,j as PreviewFetchError,b as authorizePreviewRequest,bind,bindByPath,createPreviewBindings,z as definePreview,c as extractCookie,$ as hasPreviewIntent,isAuthorizedPreviewContext,g as issuePreviewToken};
1
+ var e="payload-live-preview.authorized-preview-context",t=Symbol.for(e);function isAuthorizedPreviewContext(e){return"object"==typeof e&&null!==e&&!0===e[t]&&Object.isFrozen(e)}function r(r){return Object.freeze({...r,scope:Object.freeze({...r.scope}),payloadHeaders:Object.freeze({...r.payloadHeaders}),[t]:!0,[e]:!0})}var n=class extends Error{name="PreviewConfigurationError"};function o(e){return{authorized:!1,outcome:e,context:null}}var a="users",i=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/,s=/^[A-Za-z0-9._~-]+$/;function u(e,t,r=4096){if(null===e||0===e.length)return null;const n=`${t}=`,o=[];for(const t of e.split(";")){const e=t.trim();e.startsWith(n)&&o.push(e.slice(n.length))}if(1!==o.length)return null;const a=o[0]??"";return 0===a.length||a.length>r||!s.test(a)?null:a}var c,l="live-preview",d="v1";function p(){return c??=new TextEncoder,c}function f(e){const t="string"==typeof e?p().encode(e):e;if(t.byteLength<32)throw new n(`payload-live-preview: the preview token secret must be at least ${String(32)} bytes`);return t}function h(e){if(void 0!==e)return e;const t=globalThis.crypto;return"object"==typeof t?.subtle?t:void 0}async function y(e,t){return e.subtle.importKey("raw",t,{name:"HMAC",hash:"SHA-256"},!1,["sign","verify"])}function v(e){let t="";for(const r of e)t+=String.fromCharCode(r);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function w(e){if(!/^[A-Za-z0-9_-]*$/.test(e))return;const t=e.replace(/-/g,"+").replace(/_/g,"/")+"=".repeat((4-e.length%4)%4);try{const e=atob(t),r=new Uint8Array(e.length);for(let t=0;t<e.length;t+=1)r[t]=e.charCodeAt(t);return r}catch{return}}async function g(e,t){const r=h(t.crypto);if(void 0===r)throw new Error("payload-live-preview: no Web Crypto implementation available to sign tokens");const n=f(t.secret),o=(t.now??Date.now)(),a=Math.min(36e5,Math.max(1,e.ttlMs??6e5)),i=new Uint8Array(16);r.getRandomValues(i);const s={v:1,aud:e.audience,...void 0!==e.path?{pth:e.path}:{},...void 0!==e.locale?{loc:e.locale}:{},...void 0!==e.subject?{sub:e.subject}:{},pur:e.purpose??l,iat:o,exp:o+a,jti:v(i)},u=v(p().encode(JSON.stringify(s))),c=`${d}.${u}`,w=await y(r,n);return`${c}.${v(new Uint8Array(await r.subtle.sign("HMAC",w,p().encode(c))))}`}async function b(e,t){switch(t.type){case"payload-session":return async function(e,t){const s=function(e){const t=e??"payload-token";if(!i.test(t))throw new n(`payload-live-preview: cookie name "${t}" is not a valid cookie-name token`);return t}(t.cookieName),c=function(e){let t;try{t=new URL(e)}catch{throw new n(`payload-live-preview: serverURL "${e}" is not an absolute URL`)}if("https:"!==t.protocol&&"http:"!==t.protocol)throw new n(`payload-live-preview: serverURL must be http(s), got "${t.protocol}"`);return t.origin}(t.serverURL),l=u(e.headers.get("cookie"),s,t.maxCookieLength??4096);if(null===l)return o("missing-credential");const d=t.now??Date.now,p=Math.max(250,t.timeoutMs??3e3),f=t.fetch??globalThis.fetch,h=encodeURIComponent(t.usersSlug??a),y=`${s}=${l}`;let v;try{const e=await f(`${c}/api/${h}/me?depth=0`,{headers:{cookie:y,accept:"application/json"},signal:AbortSignal.timeout(p)});if(!e.ok)return o(401===e.status?"invalid":"unavailable");v=await e.json()}catch{return o("unavailable")}const w=function(e,t){if("object"!=typeof e||null===e||Array.isArray(e))return null;const r=e.user;if("object"!=typeof r||null===r||Array.isArray(r))return null;const n=e.collection??r.collection;if(void 0!==n&&n!==t)return null;const o=r.id;return"string"==typeof o&&o.length>0?o:"number"==typeof o&&Number.isFinite(o)?String(o):null}(v,t.usersSlug??a);if(null===w)return o("invalid");const g=function(e){const t=e.exp;return"number"!=typeof t||!Number.isFinite(t)||t<=0?void 0:t<1e11?1e3*t:t}(v);return void 0!==g&&g<=d()?o("expired"):{authorized:!0,outcome:"authorized",context:r({strategy:"payload-session",subject:w,authorizedAt:d(),expiresAt:g,scope:{},payloadHeaders:{cookie:y}})}}(e,t);case"signed-token":return async function(e,t){const n=f(t.secret),a=h(t.crypto);if(void 0===a)return o("unavailable");const i=function(e,t){if("header"===t?.kind)return e.headers.get(t.name??"x-preview-token");let r;try{r=new URL(e.url)}catch{return null}const n=r.searchParams.getAll(t?.param??"previewToken");return 1===n.length?n[0]??null:null}(e,t.transport);if(null===i||0===i.length)return o("missing-credential");if(i.length>4096)return o("invalid");const s=i.split(".");if(3!==s.length||s[0]!==d)return o("invalid");const u=s[1]??"",c=w(u),v=w(s[2]??"");if(void 0===c||32!==v?.byteLength)return o("invalid");let g;try{const e=await y(a,n);g=await a.subtle.verify("HMAC",e,v,p().encode(`${d}.${u}`))}catch{return o("unavailable")}if(!g)return o("invalid");const b=function(e){let t;try{t=JSON.parse((new TextDecoder).decode(e))}catch{return}if("object"!=typeof t||null===t)return;const r=t,n=e=>void 0===r[e]||"string"==typeof r[e];return 1===r.v&&"string"==typeof r.aud&&"string"==typeof r.pur&&"number"==typeof r.iat&&"number"==typeof r.exp&&"string"==typeof r.jti&&n("pth")&&n("loc")&&n("sub")?r:void 0}(c);if(void 0===b)return o("invalid");const m=(t.now??Date.now)();if(b.exp<=m||b.iat>b.exp)return o("expired");if(b.aud!==t.audience)return o("wrong-audience");if(b.pur!==(t.purpose??l))return o("wrong-purpose");if(void 0!==b.pth){let t;try{t=new URL(e.url).pathname}catch{return o("invalid")}if(b.pth!==t)return o("wrong-path")}if(void 0!==b.loc){const r=t.locale?.(e);if(r!==b.loc)return o("wrong-locale")}if(void 0!==t.replay){let e;try{e=await async function(e,t,r){return"consume"in e?!0===await e.consume(t,r):!await e.isUsed(t)&&(await e.markUsed(t,r),!0)}(t.replay,b.jti,b.exp)}catch{return o("unavailable")}if(!e)return o("replayed")}return{authorized:!0,outcome:"authorized",context:r({strategy:"signed-token",subject:b.sub,authorizedAt:m,expiresAt:b.exp,scope:{audience:b.aud,...void 0!==b.pth?{path:b.pth}:{},...void 0!==b.loc?{locale:b.loc}:{}},payloadHeaders:{}})}}(e,t);case"verifier":return async function(e,t){let n;try{n=await t.verify(e)}catch{return o("unavailable")}if(null===n)return o("invalid");const a=(t.now??Date.now)();return void 0!==n.expiresAt&&n.expiresAt<=a?o("expired"):{authorized:!0,outcome:"authorized",context:r({strategy:"verifier",subject:n.subject,authorizedAt:a,expiresAt:n.expiresAt,scope:n.scope??{},payloadHeaders:n.payloadHeaders??{}})}}(e,t)}}var m=["preview","draft","livePreview"];function $(e,t={}){const r=new Set(t.signals??["query","fetch-dest","referer"]),n=t.queryParams??m;if(r.has("query")){let t;try{t=new URL(e.url)}catch{t=void 0}if(void 0!==t)for(const e of n){const r=t.searchParams.get(e);if("true"===r||"1"===r)return!0}}if(r.has("fetch-dest")&&"iframe"===e.headers.get("sec-fetch-dest"))return!0;const o=t.allowedOrigins??t.adminOrigins??[],a=r.has("referer")?o:[];if(a.length>0){const t=e.headers.get("referer");if(null!==t)try{const e=new URL(t).origin;for(const t of a)try{if(new URL(t).origin===e)return!0}catch{}}catch{}}return!1}function bind(e,t){return A(e,t)}function bindByPath(e,t){const r=function(e){const t=[],r=new Proxy({},{get(e,n){if("symbol"!=typeof n)return/^\d+$/.test(n)||t.push(n),r}});try{e(r)}catch{}return t}(e);if(0===r.length)throw new Error("bindByPath: the picker did not read any field — return data.<field> instead of a constant");return A(r.join("."),t)}function A(e,t){if(0===e.length)throw new Error("bind: field name must be a non-empty string");const r={"data-payload-field":e};return void 0!==t?.attribute&&(r["data-payload-attribute"]=t.attribute),void 0!==t?.type&&(r["data-payload-type"]=t.type),!0===t?.richtext&&(r["data-payload-richtext"]=""),!0===t?.html&&(r["data-payload-html"]=""),void 0!==t?.locale&&(r["data-payload-locale"]=t.locale),void 0!==t?.alt&&(r["data-payload-alt"]=t.alt),void 0!==t?.href&&(r["data-payload-href"]=t.href),void 0!==t?.arrayTemplate&&(r["data-payload-array-template"]=t.arrayTemplate),r}var x=/^[a-z][a-z0-9-]{0,63}$/u,j=Object.freeze({});function createPreviewBindings(e){const t=isAuthorizedPreviewContext(e.authorization),r=void 0!==e.owner&&e.owner.length>0?e.owner:void 0;return Object.freeze({authorized:t,bind:(e,r)=>t?bind(e,r):j,bindByPath:(e,r)=>t?bindByPath(e,r):j,owner:()=>t&&void 0!==r?{"data-payload-owner":r}:j,boundary:(e,r={})=>{const n=function(e,t){if(!x.test(e))throw new RangeError(`createPreviewBindings().boundary(): "${e}" is not a registry id — lowercase, starting with a letter, then letters, digits or hyphens (max 64). The runtime would post it and the endpoint would refuse it.`);const r=t.key;if(void 0!==r&&(0===r.length||r.length>128))throw new RangeError(`createPreviewBindings().boundary(): the key for "${e}" must be 1 to ${String(128)} characters.`);const n=t.dependsOn??[];return{"data-payload-fragment":e,...void 0!==r?{"data-payload-fragment-key":r}:{},...n.length>0?{"data-payload-depends":n.join(",")}:{}}}(e,r);return t?n:j}})}function z(e,t={}){return createPreviewBindings({authorization:("object"==typeof e&&null!==e?e.livePreviewAuthorization:void 0)??null,...void 0!==t.owner?{owner:t.owner}:{}})}var k=class extends Error{reason;status;url;constructor(e,t,r,n){super(`payload-live-preview: preview read failed (${e}${void 0===r?"":` ${String(r)}`}) for ${t}`,{cause:n}),this.name="PreviewFetchError",this.reason=e,this.status=r,this.url=t}};function R(e){const t=function(e){let t;try{t=new URL(e)}catch{throw new Error(`payload-live-preview: serverURL "${e}" is not an absolute URL`)}if("https:"!==t.protocol&&"http:"!==t.protocol)throw new Error(`payload-live-preview: serverURL must be http(s), got "${t.protocol}"`);return`${t.origin}${t.pathname.replace(/\/+$/,"")}`}(e.serverURL),r=e.apiRoute??"/api",n=r.startsWith("/")?r:`/${r}`;if(!Number.isInteger(e.depth)||e.depth<0)throw new Error(`payload-live-preview: depth must be a non-negative integer, got ${String(e.depth)}`);const o=Math.max(250,e.timeoutMs??5e3),a=Object.freeze({serverURL:t,apiRoute:n,depth:e.depth,timeoutMs:o}),i=Object.freeze({serverURL:t,apiRoute:n,mergeDepth:e.depth}),s=`${t}${n}`,u={config:e,timeoutMs:o};return Object.freeze({config:a,runtimeOptions:i,async fetchDocument(t){const r=U(t),n=P(e.depth,t,r);n.set("limit","1"),void 0!==t.where&&O(n,t.where,["where"]);const o=`${s}/${encodeURIComponent(t.collection)}?${n.toString()}`,a=await S(u,o,t,r);if(!a.ok)return a;const i=a.data?.docs?.[0];return{ok:!0,data:i??null,draft:r,status:a.status}},async fetchGlobal(t){const r=U(t),n=P(e.depth,t,r),o=`${s}/globals/${encodeURIComponent(t.global)}?${n.toString()}`;return S(u,o,t,r)}})}function U(e){return isAuthorizedPreviewContext(e.authorization)}function P(e,t,r){const n=new URLSearchParams;return n.set("depth",String(e)),r&&n.set("draft","true"),void 0!==t.locale&&n.set("locale",t.locale),n}async function S({config:e,timeoutMs:t},r,n,o){const a=e.fetch??("function"==typeof fetch?fetch:void 0),i={config:e,url:r,draft:o,errorMode:n.errorMode??"result",startedAt:Date.now()};if(void 0===a)return L(i,{ok:!1,reason:"no-fetch",status:void 0,draft:o,cause:void 0});const s=AbortSignal.timeout(t),u=void 0===n.signal?s:AbortSignal.any([s,n.signal]),c=isAuthorizedPreviewContext(n.authorization)?n.authorization:null;let l,d;try{l=await a(r,{headers:{accept:"application/json",...n.headers??{},...c?.payloadHeaders??{}},signal:u,cache:"no-store",redirect:"error"})}catch(e){return L(i,{ok:!1,reason:M(e,n.signal),status:void 0,draft:o,cause:e})}if(!l.ok)return L(i,{ok:!1,reason:"http",status:l.status,draft:o,cause:void 0});try{d=await l.json()}catch(e){return L(i,{ok:!1,reason:"invalid-json",status:l.status,draft:o,cause:e})}return L(i,{ok:!0,data:d,draft:o,status:l.status})}function L(e,t){const{config:r,url:n,draft:o}=e,a=Date.now()-e.startedAt;if(t.ok)return r.onDiagnostic?.({kind:"response",url:n,status:t.status,draft:o,durationMs:a}),t;if(r.onDiagnostic?.({kind:"failure",url:n,reason:t.reason,status:t.status,draft:o,durationMs:a}),"throw"===e.errorMode)throw new k(t.reason,n,t.status,t.cause);return t}function M(e,t){if(!0===t?.aborted)return"aborted";const r="object"==typeof e&&null!==e?e.name:void 0;return"TimeoutError"===r?"timeout":"AbortError"===r?"aborted":"network"}function C(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function E(e){return e.map((e,t)=>0===t?e:`[${e}]`).join("")}function O(e,t,r){for(const[n,o]of Object.entries(t)){const t=[...r,n];C(o)?O(e,o,t):Array.isArray(o)&&o.some(C)?o.forEach((r,n)=>{O(e,r,[...t,String(n)])}):Array.isArray(o)?e.set(E(t),o.map(String).join(",")):e.set(E(t),"object"==typeof o?"null":String(o))}}export{e as AUTHORIZED_PREVIEW_BRAND_KEY,k as PreviewFetchError,b as authorizePreviewRequest,bind,bindByPath,createPreviewBindings,R as definePreview,u as extractCookie,$ as hasPreviewIntent,isAuthorizedPreviewContext,g as issuePreviewToken,z as previewBindingsFromLocals};
2
2
  //# sourceMappingURL=server.js.map