@sanity/next-loader
Advanced tools
Comparing version 1.1.3-release.1 to 1.1.3
# Changelog | ||
## [1.1.3](https://github.com/sanity-io/visual-editing/compare/next-loader-v1.1.2...next-loader-v1.1.3) (2024-11-06) | ||
### Bug Fixes | ||
* **deps:** update dependency @sanity/client to ^6.22.3 ([0b78719](https://github.com/sanity-io/visual-editing/commit/0b7871937422d0d8dfe7851bd1603141f0c434c4)) | ||
* let `@sanity/client` handle CORS validation ([d58c949](https://github.com/sanity-io/visual-editing/commit/d58c949566d3d468ddb469bb6fb6c87892fb784c)) | ||
* report errors as a prop, instead of throwing during render ([3cf2e07](https://github.com/sanity-io/visual-editing/commit/3cf2e07c40c8dae5219232d9d6066845ac0bdd15)), closes [#2099](https://github.com/sanity-io/visual-editing/issues/2099) | ||
### Dependencies | ||
* The following workspace dependencies were updated | ||
* devDependencies | ||
* @repo/visual-editing-helpers bumped to 0.6.30 | ||
* @sanity/preview-url-secret bumped to 2.0.1 | ||
## [1.1.2](https://github.com/sanity-io/visual-editing/compare/next-loader-v1.1.1...next-loader-v1.1.2) (2024-11-04) | ||
@@ -4,0 +21,0 @@ |
@@ -47,4 +47,4 @@ import { createListenLogic, createRequestMachine, DOMAIN, MSG_HANDSHAKE_SYN, MSG_HANDSHAKE_SYN_ACK, MSG_HANDSHAKE_ACK, MSG_RESPONSE, MSG_HEARTBEAT, MSG_DISCONNECT, createNode, createNodeMachine } from "@sanity/comlink"; | ||
const { draftModeEnabled, draftModePerspective } = props, router = useRouter(), handlePerspectiveChange = useEffectEvent( | ||
(perspective, bundlesPerspective, signal) => { | ||
draftModeEnabled && perspective !== draftModePerspective && setPerspectiveCookie(perspective, bundlesPerspective).then(() => { | ||
(perspective, signal) => { | ||
draftModeEnabled && perspective !== draftModePerspective && setPerspectiveCookie(perspective).then(() => { | ||
signal.aborted || router.refresh(); | ||
@@ -66,3 +66,3 @@ }).catch((reason) => console.error("Failed to set the preview perspective cookie", reason)); | ||
comlink.on("loader/perspective", (data) => { | ||
controller?.abort(), controller = new AbortController(), handlePerspectiveChange(data.perspective, data.bundlePerspective, controller.signal); | ||
controller?.abort(), controller = new AbortController(), handlePerspectiveChange(data.perspective, controller.signal); | ||
}); | ||
@@ -69,0 +69,0 @@ const stop = comlink.start(); |
@@ -1,4 +0,3 @@ | ||
const perspectiveCookieName = "sanity-preview-perspective", bundlePerspectiveCookieName = "sanity-preview-bundle-perspective"; | ||
const perspectiveCookieName = "sanity-preview-perspective"; | ||
function sanitizePerspective(perspective, fallback) { | ||
if (perspective?.startsWith("bundle.")) return perspective; | ||
switch (perspective) { | ||
@@ -12,11 +11,3 @@ case "previewDrafts": | ||
} | ||
function getBundlePerspective(perspective, bundlePerspective) { | ||
if (perspective === "published") return ["published"]; | ||
if (perspective === "previewDrafts") return ["drafts"]; | ||
if (perspective.startsWith("bundle.")) return bundlePerspective; | ||
throw new Error(`Invalid perspective: ${perspective}`); | ||
} | ||
export { | ||
bundlePerspectiveCookieName, | ||
getBundlePerspective, | ||
perspectiveCookieName, | ||
@@ -23,0 +14,0 @@ sanitizePerspective |
@@ -1,2 +0,2 @@ | ||
import {InitializedClientConfig} from '@sanity/client' | ||
import {ClientPerspective, InitializedClientConfig} from '@sanity/client' | ||
@@ -28,3 +28,3 @@ /** | ||
draftModeEnabled: boolean | ||
draftModePerspective?: string | ||
draftModePerspective?: ClientPerspective | ||
refreshOnMount?: boolean | ||
@@ -34,4 +34,9 @@ refreshOnFocus?: boolean | ||
tag: string | ||
/** | ||
* Handle errors from the Live Events subscription. | ||
* By default it's reported using `console.error`, you can override this prop to handle it in your own way. | ||
*/ | ||
onError?: (error: unknown) => void | ||
} | ||
export {} |
@@ -7,6 +7,13 @@ "use client"; | ||
import { useRouter } from "next/navigation.js"; | ||
import { useState, useEffect, useMemo, useRef } from "react"; | ||
import { useMemo, useEffect, useState, useRef } from "react"; | ||
import { useEffectEvent } from "use-effect-event"; | ||
import { setPerspective, setEnvironment } from "../_chunks-es/context.js"; | ||
const PresentationComlink = dynamic(() => import("../_chunks-es/PresentationComlink.js"), { ssr: !1 }), RefreshOnMount = dynamic(() => import("../_chunks-es/RefreshOnMount.js"), { ssr: !1 }), RefreshOnFocus = dynamic(() => import("../_chunks-es/RefreshOnFocus.js"), { ssr: !1 }), RefreshOnReconnect = dynamic(() => import("../_chunks-es/RefreshOnReconnect.js"), { ssr: !1 }), isMaybePreviewIframe = () => window !== window.parent, isMaybePreviewWindow = () => !!window.opener, isMaybePresentation = () => isMaybePreviewIframe() || isMaybePreviewWindow(); | ||
import { isCorsOriginError } from "../_chunks-es/isCorsOriginError.js"; | ||
const PresentationComlink = dynamic(() => import("../_chunks-es/PresentationComlink.js"), { ssr: !1 }), RefreshOnMount = dynamic(() => import("../_chunks-es/RefreshOnMount.js"), { ssr: !1 }), RefreshOnFocus = dynamic(() => import("../_chunks-es/RefreshOnFocus.js"), { ssr: !1 }), RefreshOnReconnect = dynamic(() => import("../_chunks-es/RefreshOnReconnect.js"), { ssr: !1 }), isMaybePreviewIframe = () => window !== window.parent, isMaybePreviewWindow = () => !!window.opener, isMaybePresentation = () => isMaybePreviewIframe() || isMaybePreviewWindow(), handleError = (error) => { | ||
isCorsOriginError(error) ? console.warn( | ||
"Sanity Live is unable to connect to the Sanity API as it's not in the list over allowed origins for your project.", | ||
error.addOriginUrl && "Add it here:", | ||
error.addOriginUrl?.toString() | ||
) : console.error(error); | ||
}; | ||
function SanityLive(props) { | ||
@@ -27,13 +34,5 @@ const { | ||
refreshOnReconnect = !0, | ||
tag | ||
} = props, [error, setError] = useState(null); | ||
if (error) | ||
throw error; | ||
useEffect(() => { | ||
process.env.NODE_ENV !== "production" && console.info( | ||
"Sanity is live with", | ||
token ? "automatic revalidation for draft content changes as well as published content" : draftModeEnabled ? "automatic revalidation for only published content. Provide a `browserToken` to `defineLive` to support draft content outside of Presentation Tool." : "automatic revalidation of published content" | ||
); | ||
}, [draftModeEnabled, token]); | ||
const client = useMemo( | ||
tag, | ||
onError = handleError | ||
} = props, client = useMemo( | ||
() => createClient({ | ||
@@ -51,47 +50,21 @@ projectId, | ||
[apiHost, apiVersion, dataset, projectId, requestTagPrefix, token, useProjectHostname] | ||
), router = useRouter(), handleLiveEvent = useEffectEvent( | ||
(event) => { | ||
process.env.NODE_ENV !== "production" && event.type === "welcome" ? console.info( | ||
"Sanity is live with", | ||
token ? "automatic revalidation for draft content changes as well as published content" : draftModeEnabled ? "automatic revalidation for only published content. Provide a `browserToken` to `defineLive` to support draft content outside of Presentation Tool." : "automatic revalidation of published content" | ||
) : event.type === "message" ? revalidateSyncTags(event.tags) : event.type === "restart" && router.refresh(); | ||
} | ||
); | ||
useEffect(() => { | ||
const path = client.getDataUrl("live/events"), url = new URL(client.getUrl(path, !1)), preflightTag = [requestTagPrefix, tag, "cors-preflight"].filter(Boolean).join("."); | ||
preflightTag && url.searchParams.set("tag", preflightTag), token && url.searchParams.set("includeDrafts", "true"); | ||
const controller = new AbortController(); | ||
async function validateConnection(signal) { | ||
const response = await fetch(url, { | ||
signal, | ||
headers: token ? { | ||
authorization: `Bearer ${token}` | ||
} : void 0 | ||
}); | ||
if (!response.ok) | ||
throw response.status === 401 && token ? new Error(`Failed to connect to '${url}', invalid browser token`, { | ||
cause: response.statusText | ||
}) : new Error(`Failed to connect to '${url}': ${response.status}`, { | ||
cause: response.statusText | ||
}); | ||
const reader = response.body?.getReader(); | ||
if (!reader) | ||
throw new Error("Failed to get stream reader"); | ||
try { | ||
const { done, value } = await reader.read(); | ||
if (done || !value) | ||
throw new Error("Stream ended without data"); | ||
} finally { | ||
reader.cancel(), controller.abort(); | ||
} | ||
} | ||
return validateConnection(controller.signal).catch((error2) => { | ||
error2?.name !== "AbortError" && (console.error("Error validating EventSource URL:", error2), setError(error2)); | ||
}), () => controller.abort(); | ||
}, [tag, client, requestTagPrefix, token]); | ||
const router = useRouter(), handleLiveEvent = useEffectEvent((event) => { | ||
event.type === "message" ? revalidateSyncTags(event.tags) : event.type === "restart" && router.refresh(); | ||
}); | ||
useEffect(() => { | ||
const subscription = client.live.events({ includeDrafts: !!token, tag }).subscribe({ | ||
next: (event) => { | ||
(event.type === "message" || event.type === "restart") && handleLiveEvent(event); | ||
(event.type === "message" || event.type === "restart" || event.type === "welcome") && handleLiveEvent(event); | ||
}, | ||
error: setError | ||
error: (err) => { | ||
onError(err); | ||
} | ||
}); | ||
return () => subscription.unsubscribe(); | ||
}, [client.live, handleLiveEvent, tag, token]), useEffect(() => { | ||
}, [client.live, handleLiveEvent, onError, tag, token]), useEffect(() => { | ||
draftModeEnabled && draftModePerspective ? setPerspective(draftModePerspective) : setPerspective("unknown"); | ||
@@ -98,0 +71,0 @@ }, [draftModeEnabled, draftModePerspective]); |
@@ -5,2 +5,3 @@ import { | ||
ContentSourceMap, | ||
CorsOriginError, | ||
QueryParams, | ||
@@ -10,2 +11,4 @@ SanityClient, | ||
export {CorsOriginError} | ||
/** | ||
@@ -54,2 +57,7 @@ * @public | ||
tag?: string | ||
/** | ||
* Handle errors from the Live Events subscription. | ||
* By default it's reported using `console.error`, you can override this prop to handle it in your own way. | ||
*/ | ||
onError?: (error: unknown) => void | ||
} | ||
@@ -123,2 +131,5 @@ | ||
/** @public */ | ||
export declare function isCorsOriginError(error: unknown): error is CorsOriginError | ||
export {} |
import { jsx, Fragment } from "react/jsx-runtime"; | ||
import SanityLiveClientComponent from "@sanity/next-loader/client-components/live"; | ||
import SanityLiveStreamClientComponent from "@sanity/next-loader/client-components/live-stream"; | ||
import { perspectiveCookieName, sanitizePerspective, bundlePerspectiveCookieName, getBundlePerspective } from "./_chunks-es/utils.js"; | ||
import { perspectiveCookieName, sanitizePerspective } from "./_chunks-es/utils.js"; | ||
import { draftMode, cookies } from "next/headers.js"; | ||
import { isCorsOriginError } from "./_chunks-es/isCorsOriginError.js"; | ||
function defineLive(config) { | ||
@@ -17,5 +18,3 @@ const { client: _client, serverToken, browserToken, fetchOptions } = config; | ||
allowReconfigure: !1, | ||
useCdn: !1, | ||
// @TODO should not be necessary to set the perspective to undefined | ||
perspective: void 0 | ||
useCdn: !1 | ||
}), { token: originalToken } = client.config(), sanityFetch = async function({ | ||
@@ -28,22 +27,8 @@ query, | ||
}) { | ||
const { isEnabled: isDraftModeEnabled } = _perspective !== "published" ? await draftMode() : { isEnabled: !1 }, stega = _stega ?? isDraftModeEnabled, perspective = _perspective ?? (isDraftModeEnabled ? (await cookies()).has(perspectiveCookieName) ? sanitizePerspective( | ||
const stega = _stega ?? (await draftMode()).isEnabled, perspective = _perspective ?? ((await draftMode()).isEnabled ? (await cookies()).has(perspectiveCookieName) ? sanitizePerspective( | ||
(await cookies()).get(perspectiveCookieName)?.value, | ||
"previewDrafts" | ||
) : "previewDrafts" : "published"); | ||
let rawBundlePerspective = []; | ||
if (isDraftModeEnabled && (await cookies()).has(bundlePerspectiveCookieName)) | ||
try { | ||
rawBundlePerspective = JSON.parse( | ||
(await cookies()).get(bundlePerspectiveCookieName)?.value || "[]" | ||
); | ||
} catch { | ||
} | ||
const bundlePerspective = getBundlePerspective( | ||
) : "previewDrafts" : "published"), { syncTags } = await client.fetch(query, await params, { | ||
filterResponse: !1, | ||
perspective, | ||
rawBundlePerspective | ||
), { syncTags } = await client.fetch(query, await params, { | ||
filterResponse: !1, | ||
// perspective: perspective as ClientPerspective, | ||
perspective: void 0, | ||
bundlePerspective, | ||
stega: !1, | ||
@@ -58,6 +43,3 @@ returnQuery: !1, | ||
filterResponse: !1, | ||
// perspective: perspective as ClientPerspective, | ||
// perspective: perspective as ClientPerspective, | ||
perspective: void 0, | ||
bundlePerspective, | ||
perspective, | ||
stega, | ||
@@ -89,3 +71,4 @@ token: perspective === "previewDrafts" && serverToken ? serverToken : originalToken, | ||
refreshOnReconnect, | ||
tag = "next-loader.live" | ||
tag = "next-loader.live", | ||
onError | ||
} = props, { | ||
@@ -118,3 +101,4 @@ projectId, | ||
refreshOnFocus, | ||
refreshOnReconnect | ||
refreshOnReconnect, | ||
onError | ||
} | ||
@@ -163,4 +147,5 @@ ); | ||
export { | ||
defineLive | ||
defineLive, | ||
isCorsOriginError | ||
}; | ||
//# sourceMappingURL=index.js.map |
@@ -5,2 +5,3 @@ import { | ||
ContentSourceMap, | ||
CorsOriginError, | ||
QueryParams, | ||
@@ -10,2 +11,4 @@ SanityClient, | ||
export {CorsOriginError} | ||
/** | ||
@@ -54,2 +57,7 @@ * @public | ||
tag?: string | ||
/** | ||
* Handle errors from the Live Events subscription. | ||
* By default it's reported using `console.error`, you can override this prop to handle it in your own way. | ||
*/ | ||
onError?: (error: unknown) => void | ||
} | ||
@@ -112,2 +120,5 @@ | ||
/** @public */ | ||
export declare function isCorsOriginError(error: unknown): error is CorsOriginError | ||
export {} |
@@ -0,1 +1,2 @@ | ||
import { isCorsOriginError } from "./_chunks-es/isCorsOriginError.js"; | ||
function defineLive(config) { | ||
@@ -5,4 +6,5 @@ throw new Error("defineLive can only be used in React Server Components"); | ||
export { | ||
defineLive | ||
defineLive, | ||
isCorsOriginError | ||
}; | ||
//# sourceMappingURL=index.server-only.js.map |
@@ -5,7 +5,4 @@ import type {SyncTag} from '@sanity/client' | ||
export declare function setPerspectiveCookie( | ||
perspective: string, | ||
bundlesPerspective: string[], | ||
): Promise<void> | ||
export declare function setPerspectiveCookie(perspective: string): Promise<void> | ||
export {} |
"use server"; | ||
import { sanitizePerspective, perspectiveCookieName, bundlePerspectiveCookieName } from "./_chunks-es/utils.js"; | ||
import { perspectiveCookieName, sanitizePerspective } from "./_chunks-es/utils.js"; | ||
import { revalidateTag } from "next/cache.js"; | ||
@@ -11,3 +11,3 @@ import { draftMode, cookies } from "next/headers.js"; | ||
} | ||
async function setPerspectiveCookie(perspective, bundlesPerspective) { | ||
async function setPerspectiveCookie(perspective) { | ||
if (!(await draftMode()).isEnabled) | ||
@@ -18,3 +18,3 @@ return; | ||
throw new Error(`Invalid perspective: ${perspective}`); | ||
(await cookies()).set(perspectiveCookieName, sanitizedPerspective, { | ||
(await cookies()).set(perspectiveCookieName, perspective, { | ||
httpOnly: !0, | ||
@@ -24,7 +24,2 @@ path: "/", | ||
sameSite: "none" | ||
}), (await cookies()).set(bundlePerspectiveCookieName, JSON.stringify(bundlesPerspective), { | ||
httpOnly: !0, | ||
path: "/", | ||
secure: !0, | ||
sameSite: "none" | ||
}); | ||
@@ -31,0 +26,0 @@ } |
{ | ||
"name": "@sanity/next-loader", | ||
"version": "1.1.3-release.1", | ||
"version": "1.1.3", | ||
"homepage": "https://github.com/sanity-io/visual-editing/tree/main/packages/next-loader#readme", | ||
@@ -85,22 +85,22 @@ "bugs": { | ||
"use-effect-event": "^1.0.2", | ||
"@sanity/comlink": "1.1.2-release.6" | ||
"@sanity/comlink": "1.1.1" | ||
}, | ||
"devDependencies": { | ||
"@sanity/client": "6.22.2-bundle-perspective-2", | ||
"@sanity/pkg-utils": "6.11.6", | ||
"@sanity/client": "^6.22.3", | ||
"@sanity/pkg-utils": "6.11.9", | ||
"@types/react": "^18.3.12", | ||
"eslint": "^8.57.1", | ||
"fast-deep-equal": "3.1.3", | ||
"next": "^15.0.1", | ||
"next": "^15.0.2", | ||
"react": "^18.3.1", | ||
"typescript": "5.6.3", | ||
"vitest": "^2.1.4", | ||
"@repo/eslint-config": "0.0.0", | ||
"@repo/package.config": "0.0.0", | ||
"@repo/eslint-config": "0.0.0", | ||
"@repo/visual-editing-helpers": "0.6.29", | ||
"@repo/prettier-config": "0.0.0", | ||
"@sanity/preview-url-secret": "2.0.1-release.6" | ||
"@sanity/preview-url-secret": "2.0.1", | ||
"@repo/visual-editing-helpers": "0.6.30" | ||
}, | ||
"peerDependencies": { | ||
"@sanity/client": "6.22.2-bundle-perspective-2", | ||
"@sanity/client": "^6.22.3", | ||
"next": "^14.1 || ^15.0.0-0", | ||
@@ -107,0 +107,0 @@ "react": "^18.3 || ^19.0.0-0" |
@@ -35,1 +35,3 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ | ||
} | ||
export * from './isCorsOriginError' |
export * from './defineLive' | ||
export * from './isCorsOriginError' |
'use server' | ||
import type {SyncTag} from '@sanity/client' | ||
import { | ||
bundlePerspectiveCookieName, | ||
perspectiveCookieName, | ||
} from '@sanity/preview-url-secret/constants' | ||
import type {ClientPerspective, SyncTag} from '@sanity/client' | ||
import {perspectiveCookieName} from '@sanity/preview-url-secret/constants' | ||
import {revalidateTag} from 'next/cache.js' | ||
@@ -30,6 +27,3 @@ import {cookies, draftMode} from 'next/headers.js' | ||
export async function setPerspectiveCookie( | ||
perspective: string, | ||
bundlesPerspective: string[], | ||
): Promise<void> { | ||
export async function setPerspectiveCookie(perspective: string): Promise<void> { | ||
if (!(await draftMode()).isEnabled) { | ||
@@ -44,3 +38,3 @@ // throw new Error('Draft mode is not enabled, setting perspective cookie is not allowed') | ||
;(await cookies()).set(perspectiveCookieName, sanitizedPerspective, { | ||
;(await cookies()).set(perspectiveCookieName, perspective satisfies ClientPerspective, { | ||
httpOnly: true, | ||
@@ -51,8 +45,2 @@ path: '/', | ||
}) | ||
;(await cookies()).set(bundlePerspectiveCookieName, JSON.stringify(bundlesPerspective), { | ||
httpOnly: true, | ||
path: '/', | ||
secure: true, | ||
sameSite: 'none', | ||
}) | ||
} | ||
@@ -59,0 +47,0 @@ |
@@ -9,4 +9,3 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ | ||
fallback: 'previewDrafts' | 'published', | ||
): ClientPerspective | `bundle.${string}` { | ||
if (perspective?.startsWith('bundle.')) return perspective as `bundle.${string}` | ||
) { | ||
switch (perspective) { | ||
@@ -20,15 +19,1 @@ case 'previewDrafts': | ||
} | ||
/** | ||
* This is a small hack, perspective=published is returning incorrect results, using it through bundlePerspective works fine. | ||
* @internal | ||
*/ | ||
export function getBundlePerspective( | ||
perspective: ClientPerspective | `bundle.${string}`, | ||
bundlePerspective: string[], | ||
): string[] { | ||
if (perspective === 'published') return ['published'] | ||
if (perspective === 'previewDrafts') return ['drafts'] | ||
if (perspective.startsWith('bundle.')) return bundlePerspective | ||
throw new Error(`Invalid perspective: ${perspective}`) | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
87
0
0
239900
2666
+ Added@sanity/client@6.28.0(transitive)
+ Added@sanity/comlink@1.1.1(transitive)
- Removed@sanity/client@6.22.2-bundle-perspective-2(transitive)
- Removed@sanity/comlink@1.1.2-release.6(transitive)
Updated@sanity/comlink@1.1.1