@prismicio/svelte 3.0.0-pr.70.0fcf71f → 3.0.0-pr.71.9e4af9f

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.
@@ -23,7 +23,9 @@
23
23
 
24
24
  const { repositoryName, routePrefix = "preview", routePrefixName = routePrefix }: Props = $props()
25
25
 
26
- const toolbarSrc = $derived(getToolbarSrc(repositoryName))
26
+ // `v=5` opts into the toolbar's modern flow, which follows the session set by
27
+ // `redirectToPreviewURL()`.
28
+ const toolbarSrc = $derived(`${getToolbarSrc(repositoryName)}&v=5`)
27
29
 
28
30
  /** Set to `true` when the next `beforeNavigate()` call should not prefix the route. */
29
31
  let endingPreview = $state(false)
@@ -45,6 +47,7 @@
45
47
  (event) => {
46
48
  event.preventDefault()
47
49
 
50
+ document.cookie = "io.prismic.previewSession=; Path=/; Max-Age=0"
48
51
  endingPreview = true
49
52
 
50
53
  goto(
@@ -2,7 +2,7 @@ import * as prismic from "@prismicio/client";
2
2
  import type { Cookies } from "@sveltejs/kit";
3
3
  export type RedirectToPreviewURLConfig = {
4
4
  /** The Prismic client configured for the preview session's repository. */
5
- client: Pick<prismic.Client, "resolvePreviewURL">;
5
+ client: Pick<prismic.Client, "resolvePreviewURL" | "documentAPIEndpoint">;
6
6
  /**
7
7
  * The `request` object from a `+server` file.
8
8
  *
@@ -18,26 +18,59 @@ import * as prismic from "@prismicio/client";
18
18
  * ```
19
19
  */
20
20
  export const redirectToPreviewURL = async (config) => {
21
- const previewToken = new URL(config.request.url).searchParams.get("token") ?? undefined;
22
- const documentID = new URL(config.request.url).searchParams.get("documentId") ?? undefined;
21
+ const { searchParams } = new URL(config.request.url);
22
+ const session = searchParams.get("session");
23
+ const documentID = searchParams.get("documentId") ?? undefined;
23
24
  const routePrefix = config.routePrefix ?? "preview";
24
- const previewURL = await config.client.resolvePreviewURL({
25
- previewToken,
26
- documentID,
27
- defaultURL: config.defaultURL || "/",
28
- });
25
+ // Share links from the toolbar's modern flow only carry the session.
26
+ const previewToken = searchParams.get("token") ??
27
+ (session ? await getSessionRef(config.client.documentAPIEndpoint, session) : undefined);
28
+ const path = session ? getSafePath(searchParams.get("path")) : undefined;
29
+ const location = path ??
30
+ "/" +
31
+ routePrefix +
32
+ (await config.client.resolvePreviewURL({
33
+ previewToken,
34
+ documentID,
35
+ defaultURL: config.defaultURL || "/",
36
+ }));
29
37
  // Prevent a flash of non-preview content by setting the preview token
30
38
  // on the initial page load.
31
39
  if (previewToken) {
32
- config.cookies.set(prismic.cookie.preview, previewToken, {
33
- path: "/",
34
- httpOnly: false,
35
- });
40
+ config.cookies.set(prismic.cookie.preview, previewToken, cookieOptions);
41
+ }
42
+ // The toolbar follows the preview session with this cookie.
43
+ if (session) {
44
+ config.cookies.set("io.prismic.previewSession", session, cookieOptions);
36
45
  }
37
46
  return new Response(undefined, {
38
47
  status: 307,
39
48
  headers: {
40
- Location: "/" + routePrefix + previewURL,
49
+ Location: location,
41
50
  },
42
51
  });
43
52
  };
53
+ const cookieOptions = { path: "/", httpOnly: false };
54
+ /** Returns the session's current ref from the repository's public endpoint. */
55
+ const getSessionRef = async (documentAPIEndpoint, session) => {
56
+ const url = new URL(`/previews/token/${encodeURIComponent(session)}`, documentAPIEndpoint);
57
+ url.hostname = url.hostname.replace(".cdn.", ".");
58
+ const { ref } = await (await fetch(url)).json();
59
+ if (!ref) {
60
+ throw new prismic.PreviewTokenExpiredError("The preview session has ended.", url.href, undefined);
61
+ }
62
+ return ref;
63
+ };
64
+ /**
65
+ * Returns `path` if it is a path on this website. The path already includes SvelteKit's
66
+ * `paths.base`.
67
+ */
68
+ const getSafePath = (path) => {
69
+ if (!path?.startsWith("/")) {
70
+ return;
71
+ }
72
+ // Paths like `//evil.com` and `/\evil.com` resolve to another origin.
73
+ const origin = "https://placeholder.invalid";
74
+ const url = new URL(path, origin);
75
+ return url.origin === origin ? url.pathname + url.search + url.hash : undefined;
76
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prismicio/svelte",
3
- "version": "3.0.0-pr.70.0fcf71f",
3
+ "version": "3.0.0-pr.71.9e4af9f",
4
4
  "description": "Svelte components to present Prismic content.",
5
5
  "keywords": [
6
6
  "prismic",
@@ -23,7 +23,9 @@
23
23
 
24
24
  const { repositoryName, routePrefix = "preview", routePrefixName = routePrefix }: Props = $props()
25
25
 
26
- const toolbarSrc = $derived(getToolbarSrc(repositoryName))
26
+ // `v=5` opts into the toolbar's modern flow, which follows the session set by
27
+ // `redirectToPreviewURL()`.
28
+ const toolbarSrc = $derived(`${getToolbarSrc(repositoryName)}&v=5`)
27
29
 
28
30
  /** Set to `true` when the next `beforeNavigate()` call should not prefix the route. */
29
31
  let endingPreview = $state(false)
@@ -45,6 +47,7 @@
45
47
  (event) => {
46
48
  event.preventDefault()
47
49
 
50
+ document.cookie = "io.prismic.previewSession=; Path=/; Max-Age=0"
48
51
  endingPreview = true
49
52
 
50
53
  goto(
@@ -6,7 +6,7 @@ export type RedirectToPreviewURLConfig = {
6
6
  // `Pick` is used to use the smallest possible subset of
7
7
  // `prismic.Client`. Doing this reduces the surface area for breaking
8
8
  // type changes.
9
- client: Pick<prismic.Client, "resolvePreviewURL">
9
+ client: Pick<prismic.Client, "resolvePreviewURL" | "documentAPIEndpoint">
10
10
 
11
11
  /**
12
12
  * The `request` object from a `+server` file.
@@ -50,29 +50,77 @@ export type RedirectToPreviewURLConfig = {
50
50
  export const redirectToPreviewURL = async (
51
51
  config: RedirectToPreviewURLConfig,
52
52
  ): Promise<Response> => {
53
- const previewToken = new URL(config.request.url).searchParams.get("token") ?? undefined
54
- const documentID = new URL(config.request.url).searchParams.get("documentId") ?? undefined
53
+ const { searchParams } = new URL(config.request.url)
54
+ const session = searchParams.get("session")
55
+ const documentID = searchParams.get("documentId") ?? undefined
55
56
  const routePrefix = config.routePrefix ?? "preview"
56
57
 
57
- const previewURL = await config.client.resolvePreviewURL({
58
- previewToken,
59
- documentID,
60
- defaultURL: config.defaultURL || "/",
61
- })
58
+ // Share links from the toolbar's modern flow only carry the session.
59
+ const previewToken =
60
+ searchParams.get("token") ??
61
+ (session ? await getSessionRef(config.client.documentAPIEndpoint, session) : undefined)
62
+
63
+ const path = session ? getSafePath(searchParams.get("path")) : undefined
64
+ const location =
65
+ path ??
66
+ "/" +
67
+ routePrefix +
68
+ (await config.client.resolvePreviewURL({
69
+ previewToken,
70
+ documentID,
71
+ defaultURL: config.defaultURL || "/",
72
+ }))
62
73
 
63
74
  // Prevent a flash of non-preview content by setting the preview token
64
75
  // on the initial page load.
65
76
  if (previewToken) {
66
- config.cookies.set(prismic.cookie.preview, previewToken, {
67
- path: "/",
68
- httpOnly: false,
69
- })
77
+ config.cookies.set(prismic.cookie.preview, previewToken, cookieOptions)
78
+ }
79
+
80
+ // The toolbar follows the preview session with this cookie.
81
+ if (session) {
82
+ config.cookies.set("io.prismic.previewSession", session, cookieOptions)
70
83
  }
71
84
 
72
85
  return new Response(undefined, {
73
86
  status: 307,
74
87
  headers: {
75
- Location: "/" + routePrefix + previewURL,
88
+ Location: location,
76
89
  },
77
90
  })
78
91
  }
92
+
93
+ const cookieOptions = { path: "/", httpOnly: false }
94
+
95
+ /** Returns the session's current ref from the repository's public endpoint. */
96
+ const getSessionRef = async (documentAPIEndpoint: string, session: string): Promise<string> => {
97
+ const url = new URL(`/previews/token/${encodeURIComponent(session)}`, documentAPIEndpoint)
98
+ url.hostname = url.hostname.replace(".cdn.", ".")
99
+
100
+ const { ref } = await (await fetch(url)).json()
101
+ if (!ref) {
102
+ throw new prismic.PreviewTokenExpiredError(
103
+ "The preview session has ended.",
104
+ url.href,
105
+ undefined,
106
+ )
107
+ }
108
+
109
+ return ref
110
+ }
111
+
112
+ /**
113
+ * Returns `path` if it is a path on this website. The path already includes SvelteKit's
114
+ * `paths.base`.
115
+ */
116
+ const getSafePath = (path: string | null): string | undefined => {
117
+ if (!path?.startsWith("/")) {
118
+ return
119
+ }
120
+
121
+ // Paths like `//evil.com` and `/\evil.com` resolve to another origin.
122
+ const origin = "https://placeholder.invalid"
123
+ const url = new URL(path, origin)
124
+
125
+ return url.origin === origin ? url.pathname + url.search + url.hash : undefined
126
+ }