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.
- package/README.md +60 -775
- package/dist/adapters/astro/PreviewBoundary.astro +2 -2
- package/dist/adapters/astro/index.d.ts +28 -154
- package/dist/adapters/astro/index.js +1 -1
- package/dist/adapters/astro/index.js.map +1 -1
- package/dist/adapters/astro/middleware-entry.d.ts +3 -2
- package/dist/adapters/astro/middleware-entry.js +1 -1
- package/dist/adapters/astro/middleware-entry.js.map +1 -1
- package/dist/adapters/nextjs/index.d.ts +247 -6
- package/dist/adapters/nextjs/index.js +1 -1
- package/dist/adapters/nextjs/index.js.map +1 -1
- package/dist/adapters/nuxt/index.d.ts +58 -3
- package/dist/adapters/nuxt/index.js +1 -1
- package/dist/adapters/nuxt/index.js.map +1 -1
- package/dist/adapters/nuxt/module.d.ts +82 -0
- package/dist/adapters/nuxt/module.js +2 -0
- package/dist/adapters/nuxt/module.js.map +1 -0
- package/dist/adapters/react/index.d.ts +110 -0
- package/dist/adapters/react/index.js +3 -0
- package/dist/adapters/react/index.js.map +1 -0
- package/dist/adapters/sveltekit/index.d.ts +65 -7
- package/dist/adapters/sveltekit/index.js +1 -1
- package/dist/adapters/sveltekit/index.js.map +1 -1
- package/dist/adapters/vue/index.d.ts +56 -0
- package/dist/adapters/vue/index.js +2 -0
- package/dist/adapters/vue/index.js.map +1 -0
- package/dist/annotate.d.ts +161 -0
- package/dist/annotate.js +2 -0
- package/dist/annotate.js.map +1 -0
- package/dist/client.cjs +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +182 -9
- package/dist/client.d.ts +182 -9
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/codegen-astro.js +1 -1
- package/dist/codegen-astro.js.map +1 -1
- package/dist/codegen-cli.js +1 -1
- package/dist/codegen-cli.js.map +1 -1
- package/dist/codegen.cjs +1 -1
- package/dist/codegen.cjs.map +1 -1
- package/dist/codegen.d.cts +129 -4
- package/dist/codegen.d.ts +129 -4
- package/dist/codegen.js +1 -1
- package/dist/codegen.js.map +1 -1
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +264 -73
- package/dist/core.d.ts +264 -73
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/doctor-cli.d.ts +1 -1
- package/dist/doctor-cli.js +1 -1
- package/dist/doctor-cli.js.map +1 -1
- package/dist/doctor.d.ts +7 -6
- package/dist/doctor.js +1 -1
- package/dist/doctor.js.map +1 -1
- package/dist/document-session-BfBfLVpg.d.ts +53 -0
- package/dist/fragment-endpoint-3Mlz7GDz.d.ts +189 -0
- package/dist/fragment.cjs +1 -1
- package/dist/fragment.cjs.map +1 -1
- package/dist/fragment.d.cts +64 -18
- package/dist/fragment.d.ts +64 -18
- package/dist/fragment.js +1 -1
- package/dist/fragment.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +422 -83
- package/dist/index.d.ts +422 -83
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lean.cjs +2 -0
- package/dist/lean.cjs.map +1 -0
- package/dist/lean.d.cts +44 -0
- package/dist/lean.d.ts +44 -0
- package/dist/lean.js +2 -0
- package/dist/lean.js.map +1 -0
- package/dist/lexical.cjs +1 -1
- package/dist/lexical.cjs.map +1 -1
- package/dist/lexical.d.cts +19 -1
- package/dist/lexical.d.ts +19 -1
- package/dist/lexical.js +1 -1
- package/dist/lexical.js.map +1 -1
- package/dist/locals-DBXDMxOA.d.ts +45 -0
- package/dist/{middleware-CEPsY1P7.d.ts → middleware-Bx9I7PXW.d.ts} +14 -14
- package/dist/migrate.d.ts +4 -3
- package/dist/migrate.js +1 -1
- package/dist/migrate.js.map +1 -1
- package/dist/{options-CEzzfHJu.d.ts → options-BW1eqXvc.d.ts} +90 -2
- package/dist/plugins.cjs +1 -1
- package/dist/plugins.cjs.map +1 -1
- package/dist/plugins.d.cts +111 -6
- package/dist/plugins.d.ts +111 -6
- package/dist/plugins.js +1 -1
- package/dist/plugins.js.map +1 -1
- package/dist/{probe-CYtvj_Lm.d.ts → probe-CELeUciC.d.ts} +26 -4
- package/dist/server.cjs +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +67 -13
- package/dist/server.d.ts +67 -13
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/structural.cjs +2 -2
- package/dist/structural.cjs.map +1 -1
- package/dist/structural.d.cts +47 -4
- package/dist/structural.d.ts +47 -4
- package/dist/structural.js +2 -2
- package/dist/structural.js.map +1 -1
- 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
|
|
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
|
-
* `
|
|
317
|
-
* read
|
|
318
|
-
*
|
|
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
|
|
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
|
|
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
|
-
* `
|
|
317
|
-
* read
|
|
318
|
-
*
|
|
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
|
|
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
|