payload-live-preview 2.0.6 → 2.1.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 +141 -38
- package/dist/adapters/astro/FragmentBridge.astro +25 -0
- package/dist/adapters/astro/PreviewBoundary.astro +2 -4
- package/dist/adapters/astro/RichText.astro +26 -5
- package/dist/adapters/astro/index.d.ts +41 -9
- 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 -3
- 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 +17 -12
- package/dist/adapters/nextjs/index.js +1 -1
- package/dist/adapters/nextjs/index.js.map +1 -1
- package/dist/adapters/nuxt/index.d.ts +11 -6
- package/dist/adapters/nuxt/index.js +1 -1
- package/dist/adapters/nuxt/index.js.map +1 -1
- package/dist/adapters/nuxt/module.d.ts +64 -13
- package/dist/adapters/nuxt/module.js +1 -1
- package/dist/adapters/nuxt/module.js.map +1 -1
- package/dist/adapters/react/index.d.ts +11 -7
- package/dist/adapters/react/index.js +1 -1
- package/dist/adapters/react/index.js.map +1 -1
- package/dist/adapters/sveltekit/index.d.ts +10 -5
- package/dist/adapters/sveltekit/index.js +1 -1
- package/dist/adapters/sveltekit/index.js.map +1 -1
- package/dist/adapters/vue/index.d.ts +10 -7
- package/dist/adapters/vue/index.js +1 -1
- package/dist/adapters/vue/index.js.map +1 -1
- package/dist/annotate.d.ts +15 -2
- package/dist/annotate.js.map +1 -1
- package/dist/client.cjs +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +367 -266
- package/dist/client.d.ts +367 -266
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/codegen-cli.js.map +1 -1
- package/dist/codegen.cjs.map +1 -1
- package/dist/codegen.d.cts +7 -1
- package/dist/codegen.d.ts +7 -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 +544 -392
- package/dist/core.d.ts +544 -392
- 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 +2 -2
- package/dist/doctor.js +1 -1
- package/dist/doctor.js.map +1 -1
- package/dist/{document-session-BfBfLVpg.d.ts → document-session-DWi9H3Qk.d.ts} +7 -0
- package/dist/{fragment-endpoint-DATKU183.d.ts → fragment-endpoint-B2qcRw5T.d.ts} +22 -7
- package/dist/fragment.cjs +1 -1
- package/dist/fragment.cjs.map +1 -1
- package/dist/fragment.d.cts +37 -16
- package/dist/fragment.d.ts +37 -16
- 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 +598 -400
- package/dist/index.d.ts +598 -400
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lean.cjs +1 -1
- package/dist/lean.cjs.map +1 -1
- package/dist/lean.d.cts +5 -2
- package/dist/lean.d.ts +5 -2
- package/dist/lean.js +1 -1
- package/dist/lean.js.map +1 -1
- package/dist/lexical.cjs +1 -1
- package/dist/lexical.cjs.map +1 -1
- package/dist/lexical.d.cts +32 -11
- package/dist/lexical.d.ts +32 -11
- package/dist/lexical.js +1 -1
- package/dist/lexical.js.map +1 -1
- package/dist/{locals-CNUGYbA_.d.ts → locals-B1mF3aTb.d.ts} +1 -1
- package/dist/{middleware-CWObDO37.d.ts → middleware-BGobLN9P.d.ts} +11 -5
- package/dist/migrate.js +1 -1
- package/dist/migrate.js.map +1 -1
- package/dist/{options-uHKrDM2U.d.ts → options-Cm8fC7dj.d.ts} +31 -1
- package/dist/payload.cjs +1 -1
- package/dist/payload.cjs.map +1 -1
- package/dist/payload.d.cts +18 -2
- package/dist/payload.d.ts +18 -2
- package/dist/payload.js +1 -1
- package/dist/payload.js.map +1 -1
- package/dist/plugin.cjs +2 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.d.cts +30 -0
- package/dist/plugin.d.ts +30 -0
- package/dist/plugin.js +2 -0
- package/dist/plugin.js.map +1 -0
- package/dist/plugins.cjs +1 -1
- package/dist/plugins.cjs.map +1 -1
- package/dist/plugins.d.cts +102 -29
- package/dist/plugins.d.ts +102 -29
- package/dist/plugins.js +1 -1
- package/dist/plugins.js.map +1 -1
- package/dist/{probe-Bzm4fYFL.d.ts → probe-DGPDsnbj.d.ts} +6 -2
- package/dist/server.cjs +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +43 -4
- package/dist/server.d.ts +43 -4
- 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 +61 -13
- package/dist/structural.d.ts +61 -13
- package/dist/structural.js +2 -2
- package/dist/structural.js.map +1 -1
- package/package.json +33 -4
package/dist/server.d.cts
CHANGED
|
@@ -24,6 +24,25 @@
|
|
|
24
24
|
*/
|
|
25
25
|
declare const AUTHORIZED_PREVIEW_BRAND_KEY = "payload-live-preview.authorized-preview-context";
|
|
26
26
|
type PreviewAuthorizationStrategyName = 'payload-session' | 'signed-token' | 'verifier';
|
|
27
|
+
/** One server-authorized Payload document, not a query selected by the browser. */
|
|
28
|
+
type AuthorizedPreviewDocument = {
|
|
29
|
+
readonly kind: 'collection';
|
|
30
|
+
readonly slug: string;
|
|
31
|
+
readonly id: string | number;
|
|
32
|
+
} | {
|
|
33
|
+
readonly kind: 'global';
|
|
34
|
+
readonly slug: string;
|
|
35
|
+
};
|
|
36
|
+
/** Opt-in data capability, enforced by the fragment endpoint and `definePreview`. */
|
|
37
|
+
interface AuthorizedPreviewPayloadScope {
|
|
38
|
+
/** Payload base URL, distinct from the preview site's audience. */
|
|
39
|
+
readonly serverURL: string;
|
|
40
|
+
/** REST prefix, default `/api`. */
|
|
41
|
+
readonly apiRoute?: string;
|
|
42
|
+
readonly document: AuthorizedPreviewDocument;
|
|
43
|
+
/** Maximum requested population depth, not a field-level ACL. */
|
|
44
|
+
readonly maxDepth: number;
|
|
45
|
+
}
|
|
27
46
|
/** What a context is bound to; every field is optional because every strategy binds differently. */
|
|
28
47
|
interface AuthorizedPreviewScope {
|
|
29
48
|
/** The site origin the credential was issued for. */
|
|
@@ -31,6 +50,8 @@ interface AuthorizedPreviewScope {
|
|
|
31
50
|
/** The request pathname the credential is valid for. */
|
|
32
51
|
readonly path?: string;
|
|
33
52
|
readonly locale?: string;
|
|
53
|
+
/** Requires a finite context expiry. Unscoped contexts keep their existing behavior. */
|
|
54
|
+
readonly payload?: AuthorizedPreviewPayloadScope;
|
|
34
55
|
}
|
|
35
56
|
/** The verdict of a successful `authorizePreviewRequest()`: frozen, branded, produced only there. Carry it, do not rebuild it. */
|
|
36
57
|
interface AuthorizedPreviewContext {
|
|
@@ -73,11 +94,15 @@ interface PreviewAuthorizationRequest {
|
|
|
73
94
|
readonly headers: {
|
|
74
95
|
get(name: string): string | null;
|
|
75
96
|
};
|
|
97
|
+
/** Request lifetime, when supplied by the caller. Session fetches follow it. */
|
|
98
|
+
readonly signal?: AbortSignal;
|
|
76
99
|
}
|
|
77
100
|
|
|
78
101
|
/**
|
|
79
102
|
* The `payload-session` strategy: forward exactly one named cookie to
|
|
80
103
|
* `GET <serverURL>/api/<usersSlug>/me` and authorize when a user comes back.
|
|
104
|
+
* A cookie-only request preserves working Payload 2 sessions. When `/me`
|
|
105
|
+
* reports no user, one retry adds an `Origin` for Payload 3's CSRF check.
|
|
81
106
|
*/
|
|
82
107
|
|
|
83
108
|
/** A `fetch`-compatible function, injectable for tests. */
|
|
@@ -97,6 +122,12 @@ interface PayloadSessionStrategy {
|
|
|
97
122
|
readonly usersSlug?: string;
|
|
98
123
|
/** Cookie name; Payload's default is `payload-token`. */
|
|
99
124
|
readonly cookieName?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Send this listed origin with the cookie from the first request. Without
|
|
127
|
+
* it, try the existing cookie-only request, then the origin of `serverURL`
|
|
128
|
+
* once when `/me` reports a null user. Draft reads keep the accepted headers.
|
|
129
|
+
*/
|
|
130
|
+
readonly origin?: string;
|
|
100
131
|
/** Upper bound for the `/me` round trip. Below 250 ms is raised to 250 ms; default 3000. */
|
|
101
132
|
readonly timeoutMs?: number;
|
|
102
133
|
/** Longest cookie value accepted; longer ones are refused as `missing-credential`. Default 4096. */
|
|
@@ -151,7 +182,7 @@ interface PreviewTokenReplayStore {
|
|
|
151
182
|
consume(id: string, expiresAt: number): Promise<boolean> | boolean;
|
|
152
183
|
}
|
|
153
184
|
/**
|
|
154
|
-
* @deprecated The
|
|
185
|
+
* @deprecated The store shape of the 2.0 beta, removed in 3.0. Its two calls cannot be one
|
|
155
186
|
* step, so two requests with the same token that arrive together both pass
|
|
156
187
|
* `isUsed` before either reaches `markUsed`. Implement
|
|
157
188
|
* {@link PreviewTokenReplayStore.consume} instead.
|
|
@@ -280,7 +311,7 @@ interface BindOptions {
|
|
|
280
311
|
readonly attribute?: string;
|
|
281
312
|
/** Explicit field type, bypassing schema detection. */
|
|
282
313
|
readonly type?: string;
|
|
283
|
-
/** Mark the binding as Lexical rich text; needed
|
|
314
|
+
/** Mark the binding as Lexical rich text; needed for an element that can hold uploads or relationships, or when the initial render is empty. */
|
|
284
315
|
readonly richtext?: boolean;
|
|
285
316
|
/** Render the value as sanitised HTML rather than text. */
|
|
286
317
|
readonly html?: boolean;
|
|
@@ -342,6 +373,12 @@ interface PreviewBindings {
|
|
|
342
373
|
* The registry behind `id` is the endpoint's (docs/hybrid.md).
|
|
343
374
|
*/
|
|
344
375
|
boundary: (id: string, options?: FragmentBoundaryOptions) => FragmentBoundaryAttributes | SuppressedBinding;
|
|
376
|
+
/**
|
|
377
|
+
* Field paths the page accounts for without binding them, and everything
|
|
378
|
+
* below them (`data-payload-covers`, ADR 0022), or nothing while
|
|
379
|
+
* unauthorized.
|
|
380
|
+
*/
|
|
381
|
+
covers: (...paths: readonly string[]) => Readonly<Record<'data-payload-covers', string>> | SuppressedBinding;
|
|
345
382
|
}
|
|
346
383
|
/** Request-scoped `bind`, `bindByPath` and `owner`, all suppressed unless `authorization` is a real context. */
|
|
347
384
|
declare function createPreviewBindings(options: PreviewBindingsOptions): PreviewBindings;
|
|
@@ -416,7 +453,7 @@ interface PreviewServerConfig {
|
|
|
416
453
|
readonly onDiagnostic?: (diagnostic: PreviewFetchDiagnostic) => void;
|
|
417
454
|
}
|
|
418
455
|
/** Why a read did not produce a document. */
|
|
419
|
-
type PreviewFetchFailureReason = 'http' | 'network' | 'timeout' | 'aborted' | 'invalid-json' | 'no-fetch';
|
|
456
|
+
type PreviewFetchFailureReason = 'http' | 'network' | 'timeout' | 'aborted' | 'invalid-json' | 'no-fetch' | 'scope';
|
|
420
457
|
type PreviewFetchResult<T> = {
|
|
421
458
|
readonly ok: true;
|
|
422
459
|
/** The document, or `null` when the query matched nothing. */
|
|
@@ -465,6 +502,8 @@ interface PreviewReadOptions {
|
|
|
465
502
|
}
|
|
466
503
|
interface ReadDocumentOptions extends PreviewReadOptions {
|
|
467
504
|
readonly collection: string;
|
|
505
|
+
/** Direct REST document read. Required with `scope.payload`; cannot be combined with `where`. */
|
|
506
|
+
readonly id?: string | number;
|
|
468
507
|
readonly where?: PreviewWhere;
|
|
469
508
|
}
|
|
470
509
|
interface ReadGlobalOptions extends PreviewReadOptions {
|
|
@@ -486,4 +525,4 @@ interface PreviewServer {
|
|
|
486
525
|
/** Bind origin, route and depth once; read documents and globals with an explicit authorization verdict. */
|
|
487
526
|
declare function definePreview(config: PreviewServerConfig): PreviewServer;
|
|
488
527
|
|
|
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 };
|
|
528
|
+
export { AUTHORIZED_PREVIEW_BRAND_KEY, type AuthorizedPreviewContext, type AuthorizedPreviewDocument, type AuthorizedPreviewPayloadScope, 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
|
@@ -24,6 +24,25 @@
|
|
|
24
24
|
*/
|
|
25
25
|
declare const AUTHORIZED_PREVIEW_BRAND_KEY = "payload-live-preview.authorized-preview-context";
|
|
26
26
|
type PreviewAuthorizationStrategyName = 'payload-session' | 'signed-token' | 'verifier';
|
|
27
|
+
/** One server-authorized Payload document, not a query selected by the browser. */
|
|
28
|
+
type AuthorizedPreviewDocument = {
|
|
29
|
+
readonly kind: 'collection';
|
|
30
|
+
readonly slug: string;
|
|
31
|
+
readonly id: string | number;
|
|
32
|
+
} | {
|
|
33
|
+
readonly kind: 'global';
|
|
34
|
+
readonly slug: string;
|
|
35
|
+
};
|
|
36
|
+
/** Opt-in data capability, enforced by the fragment endpoint and `definePreview`. */
|
|
37
|
+
interface AuthorizedPreviewPayloadScope {
|
|
38
|
+
/** Payload base URL, distinct from the preview site's audience. */
|
|
39
|
+
readonly serverURL: string;
|
|
40
|
+
/** REST prefix, default `/api`. */
|
|
41
|
+
readonly apiRoute?: string;
|
|
42
|
+
readonly document: AuthorizedPreviewDocument;
|
|
43
|
+
/** Maximum requested population depth, not a field-level ACL. */
|
|
44
|
+
readonly maxDepth: number;
|
|
45
|
+
}
|
|
27
46
|
/** What a context is bound to; every field is optional because every strategy binds differently. */
|
|
28
47
|
interface AuthorizedPreviewScope {
|
|
29
48
|
/** The site origin the credential was issued for. */
|
|
@@ -31,6 +50,8 @@ interface AuthorizedPreviewScope {
|
|
|
31
50
|
/** The request pathname the credential is valid for. */
|
|
32
51
|
readonly path?: string;
|
|
33
52
|
readonly locale?: string;
|
|
53
|
+
/** Requires a finite context expiry. Unscoped contexts keep their existing behavior. */
|
|
54
|
+
readonly payload?: AuthorizedPreviewPayloadScope;
|
|
34
55
|
}
|
|
35
56
|
/** The verdict of a successful `authorizePreviewRequest()`: frozen, branded, produced only there. Carry it, do not rebuild it. */
|
|
36
57
|
interface AuthorizedPreviewContext {
|
|
@@ -73,11 +94,15 @@ interface PreviewAuthorizationRequest {
|
|
|
73
94
|
readonly headers: {
|
|
74
95
|
get(name: string): string | null;
|
|
75
96
|
};
|
|
97
|
+
/** Request lifetime, when supplied by the caller. Session fetches follow it. */
|
|
98
|
+
readonly signal?: AbortSignal;
|
|
76
99
|
}
|
|
77
100
|
|
|
78
101
|
/**
|
|
79
102
|
* The `payload-session` strategy: forward exactly one named cookie to
|
|
80
103
|
* `GET <serverURL>/api/<usersSlug>/me` and authorize when a user comes back.
|
|
104
|
+
* A cookie-only request preserves working Payload 2 sessions. When `/me`
|
|
105
|
+
* reports no user, one retry adds an `Origin` for Payload 3's CSRF check.
|
|
81
106
|
*/
|
|
82
107
|
|
|
83
108
|
/** A `fetch`-compatible function, injectable for tests. */
|
|
@@ -97,6 +122,12 @@ interface PayloadSessionStrategy {
|
|
|
97
122
|
readonly usersSlug?: string;
|
|
98
123
|
/** Cookie name; Payload's default is `payload-token`. */
|
|
99
124
|
readonly cookieName?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Send this listed origin with the cookie from the first request. Without
|
|
127
|
+
* it, try the existing cookie-only request, then the origin of `serverURL`
|
|
128
|
+
* once when `/me` reports a null user. Draft reads keep the accepted headers.
|
|
129
|
+
*/
|
|
130
|
+
readonly origin?: string;
|
|
100
131
|
/** Upper bound for the `/me` round trip. Below 250 ms is raised to 250 ms; default 3000. */
|
|
101
132
|
readonly timeoutMs?: number;
|
|
102
133
|
/** Longest cookie value accepted; longer ones are refused as `missing-credential`. Default 4096. */
|
|
@@ -151,7 +182,7 @@ interface PreviewTokenReplayStore {
|
|
|
151
182
|
consume(id: string, expiresAt: number): Promise<boolean> | boolean;
|
|
152
183
|
}
|
|
153
184
|
/**
|
|
154
|
-
* @deprecated The
|
|
185
|
+
* @deprecated The store shape of the 2.0 beta, removed in 3.0. Its two calls cannot be one
|
|
155
186
|
* step, so two requests with the same token that arrive together both pass
|
|
156
187
|
* `isUsed` before either reaches `markUsed`. Implement
|
|
157
188
|
* {@link PreviewTokenReplayStore.consume} instead.
|
|
@@ -280,7 +311,7 @@ interface BindOptions {
|
|
|
280
311
|
readonly attribute?: string;
|
|
281
312
|
/** Explicit field type, bypassing schema detection. */
|
|
282
313
|
readonly type?: string;
|
|
283
|
-
/** Mark the binding as Lexical rich text; needed
|
|
314
|
+
/** Mark the binding as Lexical rich text; needed for an element that can hold uploads or relationships, or when the initial render is empty. */
|
|
284
315
|
readonly richtext?: boolean;
|
|
285
316
|
/** Render the value as sanitised HTML rather than text. */
|
|
286
317
|
readonly html?: boolean;
|
|
@@ -342,6 +373,12 @@ interface PreviewBindings {
|
|
|
342
373
|
* The registry behind `id` is the endpoint's (docs/hybrid.md).
|
|
343
374
|
*/
|
|
344
375
|
boundary: (id: string, options?: FragmentBoundaryOptions) => FragmentBoundaryAttributes | SuppressedBinding;
|
|
376
|
+
/**
|
|
377
|
+
* Field paths the page accounts for without binding them, and everything
|
|
378
|
+
* below them (`data-payload-covers`, ADR 0022), or nothing while
|
|
379
|
+
* unauthorized.
|
|
380
|
+
*/
|
|
381
|
+
covers: (...paths: readonly string[]) => Readonly<Record<'data-payload-covers', string>> | SuppressedBinding;
|
|
345
382
|
}
|
|
346
383
|
/** Request-scoped `bind`, `bindByPath` and `owner`, all suppressed unless `authorization` is a real context. */
|
|
347
384
|
declare function createPreviewBindings(options: PreviewBindingsOptions): PreviewBindings;
|
|
@@ -416,7 +453,7 @@ interface PreviewServerConfig {
|
|
|
416
453
|
readonly onDiagnostic?: (diagnostic: PreviewFetchDiagnostic) => void;
|
|
417
454
|
}
|
|
418
455
|
/** Why a read did not produce a document. */
|
|
419
|
-
type PreviewFetchFailureReason = 'http' | 'network' | 'timeout' | 'aborted' | 'invalid-json' | 'no-fetch';
|
|
456
|
+
type PreviewFetchFailureReason = 'http' | 'network' | 'timeout' | 'aborted' | 'invalid-json' | 'no-fetch' | 'scope';
|
|
420
457
|
type PreviewFetchResult<T> = {
|
|
421
458
|
readonly ok: true;
|
|
422
459
|
/** The document, or `null` when the query matched nothing. */
|
|
@@ -465,6 +502,8 @@ interface PreviewReadOptions {
|
|
|
465
502
|
}
|
|
466
503
|
interface ReadDocumentOptions extends PreviewReadOptions {
|
|
467
504
|
readonly collection: string;
|
|
505
|
+
/** Direct REST document read. Required with `scope.payload`; cannot be combined with `where`. */
|
|
506
|
+
readonly id?: string | number;
|
|
468
507
|
readonly where?: PreviewWhere;
|
|
469
508
|
}
|
|
470
509
|
interface ReadGlobalOptions extends PreviewReadOptions {
|
|
@@ -486,4 +525,4 @@ interface PreviewServer {
|
|
|
486
525
|
/** Bind origin, route and depth once; read documents and globals with an explicit authorization verdict. */
|
|
487
526
|
declare function definePreview(config: PreviewServerConfig): PreviewServer;
|
|
488
527
|
|
|
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 };
|
|
528
|
+
export { AUTHORIZED_PREVIEW_BRAND_KEY, type AuthorizedPreviewContext, type AuthorizedPreviewDocument, type AuthorizedPreviewPayloadScope, 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 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.getAll(e).at(-1);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};
|
|
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,...void 0===r.scope.payload?{}:{payload:Object.freeze({...r.scope.payload,document:Object.freeze({...r.scope.payload.document})})}}),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 i="users",a=/^[!#$%&'*+\-.^_`|~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 i=o[0]??"";return 0===i.length||i.length>r||!s.test(i)?null:i}function u(e,t){let r;try{r=new URL(e)}catch{throw new n(`payload-live-preview: ${t} "${e}" is not an absolute URL`)}if("https:"!==r.protocol&&"http:"!==r.protocol)throw new n(`payload-live-preview: ${t} must be http(s), got "${r.protocol}"`);return r.origin}function l(e,t){if(!function(){try{const e=globalThis.process?.env;return void 0!==e&&"production"!==e.NODE_ENV}catch{return!1}}())return;const r=globalThis,n=r.__payloadLivePreviewDeprecationsWarned??=/* @__PURE__ */new Set;if(!n.has(e)){n.add(e);try{console.warn(`[payload-live-preview] ${t}`)}catch{}}}var d,p="live-preview",f="v1";function h(){return d??=new TextEncoder,d}function v(e){const t="string"==typeof e?h().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 y(e){if(void 0!==e)return e;const t=globalThis.crypto;return"object"==typeof t?.subtle?t:void 0}async function g(e,t){return e.subtle.importKey("raw",t,{name:"HMAC",hash:"SHA-256"},!1,["sign","verify"])}function w(e){let t="";for(const r of e)t+=String.fromCharCode(r);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function b(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 m(e,t){const r=y(t.crypto);if(void 0===r)throw new Error("payload-live-preview: no Web Crypto implementation available to sign tokens");const n=v(t.secret),o=(t.now??Date.now)(),i=Math.min(36e5,Math.max(1,e.ttlMs??6e5)),a=new Uint8Array(16);r.getRandomValues(a);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??p,iat:o,exp:o+i,jti:w(a)},c=w(h().encode(JSON.stringify(s))),u=`${f}.${c}`,l=await g(r,n);return`${u}.${w(new Uint8Array(await r.subtle.sign("HMAC",l,h().encode(u))))}`}function $(e){return"string"==typeof e&&e.length>0&&"."!==e&&".."!==e&&!/[\s\p{Cc}\p{Cs}/\\?#%]/u.test(e)}function A(e){return"number"==typeof e?Number.isSafeInteger(e):$(e)}function x(e,t="/api"){if("string"!=typeof e||"string"!=typeof t)return null;try{const r=new URL(e);if(!["http:","https:"].includes(r.protocol)||r.username||r.password||r.search||r.hash)return null;const n=e.replace(/^[a-z]+:\/\/[^/]+/iu,"").replace(/\/+$/,""),o=t.replace(/^\//,"").replace(/\/$/,"");return""!==n&&!n.slice(1).split("/").every($)||!o.split("/").every($)?null:`${r.origin}${r.pathname.replace(/\/+$/,"")}/${o}`}catch{return null}}function j(e){return void 0!==e.expiresAt&&Number.isFinite(e.expiresAt)&&e.expiresAt>Date.now()}function k(e,t){return e.kind===t.kind&&e.slug===t.slug&&("global"===e.kind||A(t.id)&&String(e.id)===String(t.id))}async function z(e,t){switch(t.type){case"payload-session":return async function(e,t){const s=function(e){const t=e??"payload-token";if(!a.test(t))throw new n(`payload-live-preview: cookie name "${t}" is not a valid cookie-name token`);return t}(t.cookieName),l=u(t.serverURL,"serverURL"),d=void 0===t.origin?void 0:u(t.origin,"origin"),p=c(e.headers.get("cookie"),s,t.maxCookieLength??4096);if(null===p)return o("missing-credential");const f=t.now??Date.now,h=Math.max(250,t.timeoutMs??3e3);if(!Number.isSafeInteger(h))return o("unavailable");const v=t.fetch??globalThis.fetch,y=encodeURIComponent(t.usersSlug??i),g=`${s}=${p}`;let w={cookie:g};void 0!==d&&(w.origin=d);const b=new AbortController,m=()=>{b.abort(e.signal?.reason)},$=()=>b.signal.aborted;let A,x;e.signal?.addEventListener("abort",m,{once:!0}),!0===e.signal?.aborted&&m();try{if($())return o("unavailable");A=setTimeout(()=>{b.abort(new DOMException("The operation timed out","TimeoutError"))},h);for(let e=0;e<2;e++){const t=await v(`${l}/api/${y}/me?depth=0`,{headers:{...w,accept:"application/json"},signal:b.signal});if($())return o("unavailable");if(!t.ok)return o(401===t.status?"invalid":"unavailable");if(x=await t.json(),$())return o("unavailable");if(0!==e||void 0!==d||"object"!=typeof x||null===x||null!==x.user)break;w={cookie:g,origin:l}}}catch{return o("unavailable")}finally{void 0!==A&&clearTimeout(A),e.signal?.removeEventListener("abort",m)}const j=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}(x,t.usersSlug??i);if(null===j)return o("invalid");const k=function(e){const t=e.exp;return"number"!=typeof t||!Number.isFinite(t)||t<=0?void 0:t<1e11?1e3*t:t}(x);return void 0!==k&&k<=f()?o("expired"):{authorized:!0,outcome:"authorized",context:r({strategy:"payload-session",subject:j,authorizedAt:f(),expiresAt:k,scope:{},payloadHeaders:w})}}(e,t);case"signed-token":return async function(e,t){const n=v(t.secret),i=y(t.crypto);if(void 0===i)return o("unavailable");const a=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===a||0===a.length)return o("missing-credential");if(a.length>4096)return o("invalid");const s=a.split(".");if(3!==s.length||s[0]!==f)return o("invalid");const c=s[1]??"",u=b(c),d=b(s[2]??"");if(void 0===u||32!==d?.byteLength)return o("invalid");let w;try{const e=await g(i,n);w=await i.subtle.verify("HMAC",e,d,h().encode(`${f}.${c}`))}catch{return o("unavailable")}if(!w)return o("invalid");const m=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===m)return o("invalid");const $=(t.now??Date.now)();if(m.exp<=$||m.iat>m.exp)return o("expired");if(m.aud!==t.audience)return o("wrong-audience");if(m.pur!==(t.purpose??p))return o("wrong-purpose");if(void 0!==m.pth){let t;try{t=new URL(e.url).pathname}catch{return o("invalid")}if(m.pth!==t)return o("wrong-path")}if(void 0!==m.loc){const r=t.locale?.(e);if(r!==m.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):(l("replay-is-used","A `signed-token` replay store with `isUsed` and `markUsed` is removed in 3.0: implement `consume(id, expiresAt)`, which checks and records in one step (ADR 0026)."),!await e.isUsed(t)&&(await e.markUsed(t,r),!0))}(t.replay,m.jti,m.exp)}catch{return o("unavailable")}if(!e)return o("replayed")}return{authorized:!0,outcome:"authorized",context:r({strategy:"signed-token",subject:m.sub,authorizedAt:$,expiresAt:m.exp,scope:{audience:m.aud,...void 0!==m.pth?{path:m.pth}:{},...void 0!==m.loc?{locale:m.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");if(!(void 0===n.scope?.payload||function(e){if("object"!=typeof e||null===e)return!1;const t=e,r=t.document;return!(null===x(t.serverURL,t.apiRoute)||"number"!=typeof t.maxDepth||!Number.isSafeInteger(t.maxDepth)||t.maxDepth<0||"object"!=typeof r||null===r||!$(r.slug))&&("global"===r.kind||"collection"===r.kind&&A(r.id))}(n.scope.payload)&&Number.isFinite(n.expiresAt)))return o("invalid");const i=(t.now??Date.now)();return void 0!==n.expiresAt&&n.expiresAt<=i?o("expired"):{authorized:!0,outcome:"authorized",context:r({strategy:"verifier",subject:n.subject,authorizedAt:i,expiresAt:n.expiresAt,scope:n.scope??{},payloadHeaders:n.payloadHeaders??{}})}}(e,t)}}var R=["preview","draft","livePreview"];function U(e,t={}){const r=new Set(t.signals??["query","fetch-dest","referer"]),n=t.queryParams??R;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.getAll(e).at(-1);if("true"===r||"1"===r)return!0}}if(r.has("fetch-dest")&&"iframe"===e.headers.get("sec-fetch-dest"))return!0;const o=t.adminOrigins;void 0!==o&&l("admin-origins","`hasPreviewIntent({ adminOrigins })` is removed in 3.0: name the option `allowedOrigins`; `pll migrate` renames it (ADR 0026).");const i=t.allowedOrigins??o??[],a=r.has("referer")?i:[];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 O(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 O(r.join("."),t)}function O(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 S=/^[a-z][a-z0-9-]{0,63}$/u,P=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):P,bindByPath:(e,r)=>t?bindByPath(e,r):P,owner:()=>t&&void 0!==r?{"data-payload-owner":r}:P,boundary:(e,r={})=>{const n=function(e,t){if(!S.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:P},covers:(...e)=>{const r=function(e){for(const t of e)if(0===t.length||/[\s,]/u.test(t))throw new RangeError(`createPreviewBindings().covers(): "${t}" is not a field path — a name or a dotted path without spaces or commas.`);return{"data-payload-covers":e.join(" ")}}(e);return t&&e.length>0?r:P}})}function L(e,t={}){return createPreviewBindings({authorization:("object"==typeof e&&null!==e?e.livePreviewAuthorization:void 0)??null,...void 0!==t.owner?{owner:t.owner}:{}})}var E=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 C(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),i=Object.freeze({serverURL:t,apiRoute:n,depth:e.depth,timeoutMs:o}),a=Object.freeze({serverURL:t,apiRoute:n,mergeDepth:e.depth}),s=`${t}${n}`,c={config:Object.freeze({...e,...i}),timeoutMs:o};return Object.freeze({config:i,runtimeOptions:a,async fetchDocument(e){const t=T(e),r=void 0!==e.id,n=(!r||A(e.id)&&void 0===e.where)&&D(c.config,e,{kind:"collection",slug:e.collection,id:e.id});if(!n)return H(c,s,e,t,!1);const o=N(i.depth,e,t);r||(o.set("limit","1"),void 0!==e.where&&W(o,e.where,["where"]));const a=A(e.id)?`/${encodeURIComponent(String(e.id))}`:"",u=`${s}/${encodeURIComponent(e.collection)}${a}?${o.toString()}`;if(r)return H(c,u,e,t,n);const l=await H(c,u,e,t,n);if(!l.ok)return l;const d=l.data?.docs?.[0];return{ok:!0,data:d??null,draft:t,status:l.status}},async fetchGlobal(e){const t=T(e),r=N(i.depth,e,t),n=`${s}/globals/${encodeURIComponent(e.global)}?${r.toString()}`;return H(c,n,e,t,D(c.config,e,{kind:"global",slug:e.global}))}})}function D(e,t,r){const n=isAuthorizedPreviewContext(t.authorization)?t.authorization:null,o=n?.scope.payload;return void 0===o||null===n||j(n)&&(void 0===n.scope.locale||n.scope.locale===t.locale)&&x(e.serverURL,e.apiRoute)===x(o.serverURL,o.apiRoute)&&e.depth<=o.maxDepth&&k(o.document,r)}function M(e){return B(e,{ok:!1,reason:"scope",status:void 0,draft:e.draft,cause:void 0})}function T(e){return isAuthorizedPreviewContext(e.authorization)}function N(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 H({config:e,timeoutMs:t},r,n,o,i){const a=e.fetch??("function"==typeof fetch?fetch:void 0),s={config:e,url:r,draft:o,errorMode:n.errorMode??"result",startedAt:Date.now()};if(!i)return M(s);if(void 0===a)return B(s,{ok:!1,reason:"no-fetch",status:void 0,draft:o,cause:void 0});const c=AbortSignal.timeout(t),u=void 0===n.signal?c:AbortSignal.any([c,n.signal]),l=isAuthorizedPreviewContext(n.authorization)?n.authorization:null;let d,p;try{const e=new Headers({accept:"application/json",...n.headers});for(const[t,r]of Object.entries(l?.payloadHeaders??{}))e.set(t,r);d=await a(r,{headers:Object.fromEntries(e),signal:u,cache:"no-store",redirect:"error"})}catch(e){return B(s,{ok:!1,reason:I(e,n.signal),status:void 0,draft:o,cause:e})}if(void 0!==l?.scope.payload&&!j(l))return M(s);if(!d.ok)return B(s,{ok:!1,reason:"http",status:d.status,draft:o,cause:void 0});try{p=await d.json()}catch(e){return B(s,{ok:!1,reason:"invalid-json",status:d.status,draft:o,cause:e})}return void 0!==l?.scope.payload&&!j(l)||void 0!==l?.scope.payload&&!function(e,t){if("object"!=typeof t||null===t||Array.isArray(t))return!1;const r=t;return"global"===e.kind?!Object.hasOwn(r,"globalType")||r.globalType===e.slug:Object.hasOwn(r,"id")&&k(e,{kind:"collection",slug:e.slug,id:r.id})}(l.scope.payload.document,p)?M(s):B(s,{ok:!0,data:p,draft:o,status:d.status})}function B(e,t){const{config:r,url:n,draft:o}=e,i=Date.now()-e.startedAt;if(t.ok)return r.onDiagnostic?.({kind:"response",url:n,status:t.status,draft:o,durationMs:i}),t;if(r.onDiagnostic?.({kind:"failure",url:n,reason:t.reason,status:t.status,draft:o,durationMs:i}),"throw"===e.errorMode)throw new E(t.reason,n,t.status,t.cause);return t}function I(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 _(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function F(e){return e.map((e,t)=>0===t?e:`[${e}]`).join("")}function W(e,t,r){for(const[n,o]of Object.entries(t)){const t=[...r,n];_(o)?W(e,o,t):Array.isArray(o)&&o.some(_)?o.forEach((r,n)=>{W(e,r,[...t,String(n)])}):Array.isArray(o)?e.set(F(t),o.map(String).join(",")):e.set(F(t),"object"==typeof o?"null":String(o))}}export{e as AUTHORIZED_PREVIEW_BRAND_KEY,E as PreviewFetchError,z as authorizePreviewRequest,bind,bindByPath,createPreviewBindings,C as definePreview,c as extractCookie,U as hasPreviewIntent,isAuthorizedPreviewContext,m as issuePreviewToken,L as previewBindingsFromLocals};
|
|
2
2
|
//# sourceMappingURL=server.js.map
|