next-sanity 12.0.2 → 12.0.4
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 +0 -1
- package/dist/NextStudio.js.map +1 -1
- package/dist/PresentationComlink.js.map +1 -1
- package/dist/PresentationComlink2.js.map +1 -1
- package/dist/SanityLiveStream.js.map +1 -1
- package/dist/VisualEditing.js.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/defineLive.d.ts.map +1 -1
- package/dist/draft-mode/index.d.ts.map +1 -1
- package/dist/draft-mode/index.js.map +1 -1
- package/dist/experimental/client-components/live.d.ts.map +1 -1
- package/dist/experimental/client-components/live.js.map +1 -1
- package/dist/experimental/live.d.ts.map +1 -1
- package/dist/experimental/live.js.map +1 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/live/client-components/live/index.d.ts.map +1 -1
- package/dist/live/client-components/live/index.js.map +1 -1
- package/dist/live/client-components/live-stream/index.d.ts.map +1 -1
- package/dist/live/client-components/live-stream/index.js.map +1 -1
- package/dist/live/server-actions/index.d.ts.map +1 -1
- package/dist/live/server-actions/index.js.map +1 -1
- package/dist/live.js.map +1 -1
- package/dist/utils.js.map +1 -1
- package/dist/visual-editing/index.d.ts.map +1 -1
- package/dist/visual-editing/index.js.map +1 -1
- package/dist/visual-editing/server-actions/index.js.map +1 -1
- package/dist/webhook/index.d.ts +9 -1
- package/dist/webhook/index.d.ts.map +1 -1
- package/dist/webhook/index.js.map +1 -1
- package/package.json +10 -12
package/README.md
CHANGED
package/dist/NextStudio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NextStudio.js","names":[],"sources":["../src/studio/client-component/createHashHistoryForStudio.ts","../src/studio/client-component/useIsMounted.ts","../src/studio/client-component/NextStudio.tsx"],"sourcesContent":["// oxlint-disable unbound-method\nimport {createHashHistory, type History, type Listener} from 'history'\n\n/** @internal */\nexport function createHashHistoryForStudio(): History {\n const history = createHashHistory()\n return {\n get action() {\n return history.action\n },\n get location() {\n return history.location\n },\n get createHref() {\n return history.createHref\n },\n get push() {\n return history.push\n },\n get replace() {\n return history.replace\n },\n get go() {\n return history.go\n },\n get back() {\n return history.back\n },\n get forward() {\n return history.forward\n },\n get block() {\n return history.block\n },\n // Overriding listen to workaround a problem where native history provides history.listen(location => void), but the npm package is history.listen(({action, location}) => void)\n listen(listener: Listener) {\n // return history.listen(({ action, location }) => {\n return history.listen(({location}) => {\n // console.debug('history.listen', action, location)\n // @ts-expect-error -- working around a bug? in studio\n listener(location)\n })\n },\n }\n}\n","import {useSyncExternalStore} from 'react'\n\n/** @internal */\nexport function useIsMounted(): boolean {\n return useSyncExternalStore(\n emptySubscribe,\n () => true,\n () => false,\n )\n}\
|
|
1
|
+
{"version":3,"file":"NextStudio.js","names":[],"sources":["../src/studio/client-component/createHashHistoryForStudio.ts","../src/studio/client-component/useIsMounted.ts","../src/studio/client-component/NextStudio.tsx"],"sourcesContent":["// oxlint-disable unbound-method\nimport {createHashHistory, type History, type Listener} from 'history'\n\n/** @internal */\nexport function createHashHistoryForStudio(): History {\n const history = createHashHistory()\n return {\n get action() {\n return history.action\n },\n get location() {\n return history.location\n },\n get createHref() {\n return history.createHref\n },\n get push() {\n return history.push\n },\n get replace() {\n return history.replace\n },\n get go() {\n return history.go\n },\n get back() {\n return history.back\n },\n get forward() {\n return history.forward\n },\n get block() {\n return history.block\n },\n // Overriding listen to workaround a problem where native history provides history.listen(location => void), but the npm package is history.listen(({action, location}) => void)\n listen(listener: Listener) {\n // return history.listen(({ action, location }) => {\n return history.listen(({location}) => {\n // console.debug('history.listen', action, location)\n // @ts-expect-error -- working around a bug? in studio\n listener(location)\n })\n },\n }\n}\n","import {useSyncExternalStore} from 'react'\n\n/** @internal */\nexport function useIsMounted(): boolean {\n return useSyncExternalStore(\n emptySubscribe,\n () => true,\n () => false,\n )\n}\nconst emptySubscribe = () => () => {}\n","import {useMemo} from 'react'\nimport {Studio, type StudioProps} from 'sanity'\n\nimport {NextStudioLayout} from '../NextStudioLayout'\nimport {NextStudioNoScript} from '../NextStudioNoScript'\nimport {createHashHistoryForStudio} from './createHashHistoryForStudio'\nimport {useIsMounted} from './useIsMounted'\n\n/** @public */\nexport interface NextStudioProps extends StudioProps {\n children?: React.ReactNode\n /**\n * Render the <noscript> tag\n * @defaultValue true\n * @alpha\n */\n unstable__noScript?: boolean\n /**\n * The 'hash' option is new feature that is not yet stable for production, but is available for testing and its API won't change in a breaking way.\n * If 'hash' doesn't work for you, or if you want to use a memory based history, you can use the `unstable_history` prop instead.\n * @alpha\n * @defaultValue 'browser'\n */\n history?: 'browser' | 'hash'\n}\n/**\n * Override how the Studio renders by passing children.\n * This is useful for advanced use cases where you're using StudioProvider and StudioLayout instead of Studio:\n * ```\n * import {StudioProvider, StudioLayout} from 'sanity'\n * import {NextStudio} from 'next-sanity/studio'\n * <NextStudio config={config}>\n * <StudioProvider config={config}>\n * <CustomComponentThatUsesContextFromStudioProvider />\n * <StudioLayout />\n * </StudioProvider>\n * </NextStudio>\n * ```\n * @public\n */\nexport default function NextStudioComponent({\n children,\n config,\n unstable__noScript = true,\n scheme,\n history,\n ...props\n}: NextStudioProps): React.JSX.Element {\n const isMounted = useIsMounted()\n const unstableHistory = useMemo<typeof props.unstable_history>(() => {\n if (props.unstable_history && history) {\n throw new Error('Cannot use both `unstable_history` and `history` props at the same time')\n }\n\n if (isMounted && history === 'hash') {\n return createHashHistoryForStudio()\n }\n return props.unstable_history\n }, [history, isMounted, props.unstable_history])\n\n return (\n <>\n {unstable__noScript && <NextStudioNoScript />}\n <NextStudioLayout>\n {history === 'hash' && !isMounted\n ? null\n : children || (\n <Studio\n config={config}\n scheme={scheme}\n unstable_globalStyles\n {...props}\n unstable_history={unstableHistory}\n />\n )}\n </NextStudioLayout>\n </>\n )\n}\n"],"mappings":";;;;;AAIA,SAAgB,6BAAsC;CACpD,MAAM,UAAU,mBAAmB;AACnC,QAAO;EACL,IAAI,SAAS;AACX,UAAO,QAAQ;;EAEjB,IAAI,WAAW;AACb,UAAO,QAAQ;;EAEjB,IAAI,aAAa;AACf,UAAO,QAAQ;;EAEjB,IAAI,OAAO;AACT,UAAO,QAAQ;;EAEjB,IAAI,UAAU;AACZ,UAAO,QAAQ;;EAEjB,IAAI,KAAK;AACP,UAAO,QAAQ;;EAEjB,IAAI,OAAO;AACT,UAAO,QAAQ;;EAEjB,IAAI,UAAU;AACZ,UAAO,QAAQ;;EAEjB,IAAI,QAAQ;AACV,UAAO,QAAQ;;EAGjB,OAAO,UAAoB;AAEzB,UAAO,QAAQ,QAAQ,EAAC,eAAc;AAGpC,aAAS,SAAS;KAClB;;EAEL;;ACxCH,SAAgB,eAAwB;AACtC,QAAO,qBACL,sBACM,YACA,MACP;;AAEH,MAAM,6BAA6B;AC8BnC,SAAwB,oBAAoB,EAC1C,UACA,QACA,qBAAqB,MACrB,QACA,SACA,GAAG,SACkC;CACrC,MAAM,YAAY,cAAc;CAChC,MAAM,kBAAkB,cAA6C;AACnE,MAAI,MAAM,oBAAoB,QAC5B,OAAM,IAAI,MAAM,0EAA0E;AAG5F,MAAI,aAAa,YAAY,OAC3B,QAAO,4BAA4B;AAErC,SAAO,MAAM;IACZ;EAAC;EAAS;EAAW,MAAM;EAAiB,CAAC;AAEhD,QACE,qBAAA,UAAA,EAAA,UAAA,CACG,sBAAsB,oBAAC,oBAAA,EAAA,CAAqB,EAC7C,oBAAC,kBAAA,EAAA,UACE,YAAY,UAAU,CAAC,YACpB,OACA,YACE,oBAAC,QAAA;EACS;EACA;EACR,uBAAA;EACA,GAAI;EACJ,kBAAkB;GAClB,EAAA,CAES,CAAA,EAAA,CAClB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PresentationComlink.js","names":["controller: AbortController | undefined"],"sources":["../src/experimental/client-components/PresentationComlink.tsx"],"sourcesContent":["import type {ClientPerspective} from '@sanity/client'\nimport {createNode, createNodeMachine} from '@sanity/comlink'\nimport {
|
|
1
|
+
{"version":3,"file":"PresentationComlink.js","names":["controller: AbortController | undefined"],"sources":["../src/experimental/client-components/PresentationComlink.tsx"],"sourcesContent":["import type {ClientPerspective} from '@sanity/client'\n\nimport {createNode, createNodeMachine} from '@sanity/comlink'\nimport {\n createCompatibilityActors,\n type LoaderControllerMsg,\n type LoaderNodeMsg,\n} from '@sanity/presentation-comlink'\nimport {setPerspectiveCookie} from 'next-sanity/live/server-actions'\nimport {useRouter} from 'next/navigation'\nimport {startTransition, useEffect, useEffectEvent} from 'react'\n\nimport {\n setComlink,\n setComlinkClientConfig,\n setPerspective,\n perspective,\n} from '../../live/hooks/context'\nimport {sanitizePerspective} from '../../live/utils'\n\nexport default function PresentationComlink(props: {\n projectId: string\n dataset: string\n draftModeEnabled: boolean\n}): React.JSX.Element | null {\n const {projectId, dataset, draftModeEnabled} = props\n const router = useRouter()\n\n useEffect(() => {\n setComlinkClientConfig(projectId, dataset)\n }, [dataset, projectId])\n\n const handlePerspectiveChange = useEffectEvent(\n (_perspective: ClientPerspective, signal: AbortSignal) => {\n const nextPerspective = sanitizePerspective(_perspective, 'drafts')\n if (draftModeEnabled && perspective.toString() !== nextPerspective.toString()) {\n setPerspective(nextPerspective)\n startTransition(() =>\n setPerspectiveCookie(nextPerspective)\n .then(() => {\n if (signal.aborted) return\n router.refresh()\n })\n .catch((reason) =>\n console.error('Failed to set the preview perspective cookie', reason),\n ),\n )\n }\n },\n )\n\n useEffect(() => {\n const comlink = createNode<LoaderNodeMsg, LoaderControllerMsg>(\n {name: 'loaders', connectTo: 'presentation'},\n createNodeMachine<LoaderNodeMsg, LoaderControllerMsg>().provide({\n actors: createCompatibilityActors<LoaderNodeMsg>(),\n }),\n )\n\n let controller: AbortController | undefined\n comlink.on('loader/perspective', (data) => {\n controller?.abort()\n controller = new AbortController()\n handlePerspectiveChange(data.perspective, controller.signal)\n })\n\n const stop = comlink.start()\n setComlink(comlink)\n return () => {\n stop()\n }\n }, [])\n\n return null\n}\n"],"mappings":";;;;;;;AAoBA,SAAwB,oBAAoB,OAIf;CAC3B,MAAM,EAAC,WAAW,SAAS,qBAAoB;CAC/C,MAAM,SAAS,WAAW;AAE1B,iBAAgB;AACd,yBAAuB,WAAW,QAAQ;IACzC,CAAC,SAAS,UAAU,CAAC;CAExB,MAAM,0BAA0B,gBAC7B,cAAiC,WAAwB;EACxD,MAAM,kBAAkB,oBAAoB,cAAc,SAAS;AACnE,MAAI,oBAAoB,YAAY,UAAU,KAAK,gBAAgB,UAAU,EAAE;AAC7E,kBAAe,gBAAgB;AAC/B,yBACE,qBAAqB,gBAAgB,CAClC,WAAW;AACV,QAAI,OAAO,QAAS;AACpB,WAAO,SAAS;KAChB,CACD,OAAO,WACN,QAAQ,MAAM,gDAAgD,OAAO,CACtE,CACJ;;GAGN;AAED,iBAAgB;EACd,MAAM,UAAU,WACd;GAAC,MAAM;GAAW,WAAW;GAAe,EAC5C,mBAAuD,CAAC,QAAQ,EAC9D,QAAQ,2BAA0C,EACnD,CAAC,CACH;EAED,IAAIA;AACJ,UAAQ,GAAG,uBAAuB,SAAS;AACzC,eAAY,OAAO;AACnB,gBAAa,IAAI,iBAAiB;AAClC,2BAAwB,KAAK,aAAa,WAAW,OAAO;IAC5D;EAEF,MAAM,OAAO,QAAQ,OAAO;AAC5B,aAAW,QAAQ;AACnB,eAAa;AACX,SAAM;;IAEP,EAAE,CAAC;AAEN,QAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PresentationComlink2.js","names":["controller: AbortController | undefined"],"sources":["../src/live/client-components/live/PresentationComlink.tsx"],"sourcesContent":["import type {ClientPerspective} from '@sanity/client'\nimport {\n createNode,\n createNodeMachine,\n // type Node,\n} from '@sanity/comlink'\nimport {
|
|
1
|
+
{"version":3,"file":"PresentationComlink2.js","names":["controller: AbortController | undefined"],"sources":["../src/live/client-components/live/PresentationComlink.tsx"],"sourcesContent":["import type {ClientPerspective} from '@sanity/client'\n\nimport {\n createNode,\n createNodeMachine,\n // type Node,\n} from '@sanity/comlink'\nimport {\n createCompatibilityActors,\n type LoaderControllerMsg,\n type LoaderNodeMsg,\n} from '@sanity/presentation-comlink'\nimport {setPerspectiveCookie} from 'next-sanity/live/server-actions'\nimport {useRouter} from 'next/navigation'\nimport {useEffect, useEffectEvent} from 'react'\n\nimport {setComlink, setComlinkClientConfig} from '../../hooks/context'\n\nfunction PresentationComlink(props: {\n projectId: string\n dataset: string\n // handleDraftModeAction: (secret: string) => Promise<void | string>\n draftModeEnabled: boolean\n draftModePerspective: ClientPerspective\n}): React.JSX.Element | null {\n const {projectId, dataset, draftModeEnabled, draftModePerspective} = props\n const router = useRouter()\n\n // const [presentationComlink, setPresentationComlink] = useState<Node<\n // LoaderControllerMsg,\n // LoaderNodeMsg\n // > | null>(null)\n\n useEffect(() => {\n setComlinkClientConfig(projectId, dataset)\n }, [dataset, projectId])\n\n const handlePerspectiveChange = useEffectEvent(\n (perspective: ClientPerspective, signal: AbortSignal) => {\n if (draftModeEnabled && perspective !== draftModePerspective) {\n setPerspectiveCookie(perspective)\n .then(() => {\n if (signal.aborted) return\n router.refresh()\n })\n .catch((reason) => console.error('Failed to set the preview perspective cookie', reason))\n }\n },\n )\n\n // const [status, setStatus] = useState('disconnected')\n useEffect(() => {\n const comlink = createNode<LoaderNodeMsg, LoaderControllerMsg>(\n {\n name: 'loaders',\n connectTo: 'presentation',\n },\n createNodeMachine<LoaderNodeMsg, LoaderControllerMsg>().provide({\n actors: createCompatibilityActors<LoaderNodeMsg>(),\n }),\n )\n\n // comlink.onStatus((status) => {\n // setStatus(status)\n // })\n\n let controller: AbortController | undefined\n comlink.on('loader/perspective', (data) => {\n controller?.abort()\n controller = new AbortController()\n handlePerspectiveChange(data.perspective, controller.signal)\n })\n\n const stop = comlink.start()\n // setPresentationComlink(comlink)\n setComlink(comlink)\n // console.log('setting comlink', comlink)\n return () => {\n // console.log('stopping comlink')\n stop()\n }\n }, [])\n\n // @TODO come back to this later\n // const handleEnableDraftMode = useEffectEvent(async (signal: AbortSignal) => {\n // if (signal.aborted) return\n // const {secret} = await (presentationComlink?.fetch(\n // {\n // type: 'loader/fetch-preview-url-secret' as const,\n // data: {projectId, dataset},\n // },\n // {signal},\n // ) || {secret: null})\n // if (signal.aborted) return\n // const error = await handleDraftModeAction(secret!)\n // // eslint-disable-next-line no-console\n // // @TODO call another server action here that can tell us if draft mode is actually enabled\n // if (error) {\n // // @TODO use sonnet or whatever to push a toast with the error\n // // eslint-disable-next-line no-console\n // console.error('Error enabling draft mode', error)\n // return\n // }\n // // console.log('Draft mode enabled?', {enabled})\n // if (signal.aborted) return\n // router.refresh()\n // })\n // const connected = status === 'connected'\n // useEffect(() => {\n // if (connected && !draftModeEnabled) {\n // const controller = new AbortController()\n // handleEnableDraftMode(controller.signal).catch((reason) => {\n // // eslint-disable-next-line no-console\n // console.error('Failed to enable draft mode', reason)\n // return handleEnableDraftMode(controller.signal)\n // })\n // return () => {\n // controller.abort()\n // }\n // }\n // return undefined\n // }, [connected, draftModeEnabled, handleEnableDraftMode])\n\n return null\n}\nPresentationComlink.displayName = 'PresentationComlink'\n\nexport default PresentationComlink\n"],"mappings":";;;;;;AAkBA,SAAS,oBAAoB,OAMA;CAC3B,MAAM,EAAC,WAAW,SAAS,kBAAkB,yBAAwB;CACrE,MAAM,SAAS,WAAW;AAO1B,iBAAgB;AACd,yBAAuB,WAAW,QAAQ;IACzC,CAAC,SAAS,UAAU,CAAC;CAExB,MAAM,0BAA0B,gBAC7B,aAAgC,WAAwB;AACvD,MAAI,oBAAoB,gBAAgB,qBACtC,sBAAqB,YAAY,CAC9B,WAAW;AACV,OAAI,OAAO,QAAS;AACpB,UAAO,SAAS;IAChB,CACD,OAAO,WAAW,QAAQ,MAAM,gDAAgD,OAAO,CAAC;GAGhG;AAGD,iBAAgB;EACd,MAAM,UAAU,WACd;GACE,MAAM;GACN,WAAW;GACZ,EACD,mBAAuD,CAAC,QAAQ,EAC9D,QAAQ,2BAA0C,EACnD,CAAC,CACH;EAMD,IAAIA;AACJ,UAAQ,GAAG,uBAAuB,SAAS;AACzC,eAAY,OAAO;AACnB,gBAAa,IAAI,iBAAiB;AAClC,2BAAwB,KAAK,aAAa,WAAW,OAAO;IAC5D;EAEF,MAAM,OAAO,QAAQ,OAAO;AAE5B,aAAW,QAAQ;AAEnB,eAAa;AAEX,SAAM;;IAEP,EAAE,CAAC;AA0CN,QAAO;;AAET,oBAAoB,cAAc;AAElC,IAAA,8BAAe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SanityLiveStream.js","names":["comlink","comlinkSnapshot","children"],"sources":["../src/live/client-components/live-stream/SanityLiveStream.tsx"],"sourcesContent":["// oxlint-disable no-unsafe-type-assertion\nimport {\n type ClientPerspective,\n type ContentSourceMap,\n type InitializedClientConfig,\n type QueryParams,\n} from '@sanity/client'\nimport {stegaEncodeSourceMap} from '@sanity/client/stega'\nimport
|
|
1
|
+
{"version":3,"file":"SanityLiveStream.js","names":["comlink","comlinkSnapshot","children"],"sources":["../src/live/client-components/live-stream/SanityLiveStream.tsx"],"sourcesContent":["import type {LoaderControllerMsg} from '@sanity/presentation-comlink'\n\n// oxlint-disable no-unsafe-type-assertion\nimport {\n type ClientPerspective,\n type ContentSourceMap,\n type InitializedClientConfig,\n type QueryParams,\n} from '@sanity/client'\nimport {stegaEncodeSourceMap} from '@sanity/client/stega'\nimport {dequal} from 'dequal/lite'\nimport {use, useCallback, useEffect, useState, useSyncExternalStore, useEffectEvent} from 'react'\n\nimport {comlinkListeners, comlink as comlinkSnapshot} from '../../hooks/context'\n\n/**\n * @public\n */\nexport interface SanityLiveStreamProps extends Pick<\n InitializedClientConfig,\n 'projectId' | 'dataset'\n> {\n query: string\n params?: QueryParams\n perspective?: Exclude<ClientPerspective, 'raw'>\n stega?: boolean\n initial: Promise<React.ReactNode>\n children: (result: {\n data: unknown\n sourceMap: ContentSourceMap | null\n tags: string[]\n }) => Promise<React.ReactNode>\n}\n\nconst LISTEN_HEARTBEAT_INTERVAL = 10_000\n\n/**\n * @public\n */\nexport default function SanityLiveStream(props: SanityLiveStreamProps): React.JSX.Element | null {\n const {query, dataset, params = {}, perspective, projectId, stega} = props\n\n const subscribe = useCallback((listener: () => void) => {\n comlinkListeners.add(listener)\n return () => comlinkListeners.delete(listener)\n }, [])\n\n const comlink = useSyncExternalStore(\n subscribe,\n () => comlinkSnapshot,\n () => null,\n )\n const [children, setChildren] = useState<React.ReactNode | undefined>(undefined)\n\n const handleQueryHeartbeat = useEffectEvent((comlink: NonNullable<typeof comlinkSnapshot>) => {\n comlink.post('loader/query-listen', {\n projectId: projectId!,\n dataset: dataset!,\n perspective: perspective! as ClientPerspective,\n query,\n params: params,\n heartbeat: LISTEN_HEARTBEAT_INTERVAL,\n })\n })\n const handleQueryChange = useEffectEvent(\n (event: Extract<LoaderControllerMsg, {type: 'loader/query-change'}>['data']) => {\n if (\n dequal(\n {\n projectId,\n dataset,\n query,\n params,\n },\n {\n projectId: event.projectId,\n dataset: event.dataset,\n query: event.query,\n params: event.params,\n },\n )\n ) {\n const {result, resultSourceMap, tags} = event\n const data = stega\n ? stegaEncodeSourceMap(result, resultSourceMap, {enabled: true, studioUrl: '/'})\n : result\n // console.log('server function streaming is disabled', {\n // startTransition,\n // setPromise,\n // data,\n // resultSourceMap,\n // tags,\n // })\n // console.log('rendering with server action')\n // startTransition(() =>\n // setPromise(\n // props.children({\n // data,\n // sourceMap: resultSourceMap!,\n // tags: tags || [],\n // }) as Promise<React.JSX.Element>,\n // ),\n // )\n // oxlint-disable-next-line no-console\n console.groupCollapsed('rendering with server action')\n ;(\n props.children({\n data,\n sourceMap: resultSourceMap!,\n tags: tags || [],\n }) as Promise<React.JSX.Element>\n )\n .then(\n (children) => {\n // oxlint-disable-next-line no-console\n console.log('setChildren(children)')\n // startTransition(() => setChildren(children))\n setChildren(children)\n },\n (reason: unknown) => {\n console.error('rendering with server action: render children error', reason)\n },\n )\n // oxlint-disable-next-line no-console\n .finally(() => console.groupEnd())\n }\n },\n )\n useEffect(() => {\n if (!comlink) return\n\n const unsubscribe = comlink.on('loader/query-change', handleQueryChange)\n const interval = setInterval(() => handleQueryHeartbeat(comlink), LISTEN_HEARTBEAT_INTERVAL)\n return () => {\n clearInterval(interval)\n unsubscribe()\n }\n }, [comlink])\n\n if (!comlink || children === undefined) {\n return use(props.initial) as React.JSX.Element\n }\n\n return <>{children}</>\n}\n"],"mappings":";;;;;;AAkCA,MAAM,4BAA4B;AAKlC,SAAwB,iBAAiB,OAAwD;CAC/F,MAAM,EAAC,OAAO,SAAS,SAAS,EAAE,EAAE,aAAa,WAAW,UAAS;CAOrE,MAAMA,YAAU,qBALE,aAAa,aAAyB;AACtD,mBAAiB,IAAI,SAAS;AAC9B,eAAa,iBAAiB,OAAO,SAAS;IAC7C,EAAE,CAAC,QAIEC,eACA,KACP;CACD,MAAM,CAAC,UAAU,eAAe,SAAsC,KAAA,EAAU;CAEhF,MAAM,uBAAuB,gBAAgB,cAAiD;AAC5F,YAAQ,KAAK,uBAAuB;GACvB;GACF;GACI;GACb;GACQ;GACR,WAAW;GACZ,CAAC;GACF;CACF,MAAM,oBAAoB,gBACvB,UAA+E;AAC9E,MACE,OACE;GACE;GACA;GACA;GACA;GACD,EACD;GACE,WAAW,MAAM;GACjB,SAAS,MAAM;GACf,OAAO,MAAM;GACb,QAAQ,MAAM;GACf,CACF,EACD;GACA,MAAM,EAAC,QAAQ,iBAAiB,SAAQ;GACxC,MAAM,OAAO,QACT,qBAAqB,QAAQ,iBAAiB;IAAC,SAAS;IAAM,WAAW;IAAI,CAAC,GAC9E;AAmBJ,WAAQ,eAAe,+BAA+B;AAEpD,SAAM,SAAS;IACb;IACA,WAAW;IACX,MAAM,QAAQ,EAAE;IACjB,CAAC,CAED,MACE,eAAa;AAEZ,YAAQ,IAAI,wBAAwB;AAEpC,gBAAYC,WAAS;OAEtB,WAAoB;AACnB,YAAQ,MAAM,uDAAuD,OAAO;KAE/E,CAEA,cAAc,QAAQ,UAAU,CAAC;;GAGzC;AACD,iBAAgB;AACd,MAAI,CAACF,UAAS;EAEd,MAAM,cAAcA,UAAQ,GAAG,uBAAuB,kBAAkB;EACxE,MAAM,WAAW,kBAAkB,qBAAqBA,UAAQ,EAAE,0BAA0B;AAC5F,eAAa;AACX,iBAAc,SAAS;AACvB,gBAAa;;IAEd,CAACA,UAAQ,CAAC;AAEb,KAAI,CAACA,aAAW,aAAa,KAAA,EAC3B,QAAO,IAAI,MAAM,QAAQ;AAG3B,QAAO,oBAAA,UAAA,EAAG,UAAA,CAAY"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VisualEditing.js","names":["VisualEditingComponent"],"sources":["../src/visual-editing/client-component/utils.ts","../src/visual-editing/client-component/VisualEditing.tsx"],"sourcesContent":["/**\n * From: https://github.com/vercel/next.js/blob/5469e6427b54ab7e9876d4c85b47f9c3afdc5c1f/packages/next/src/shared/lib/router/utils/path-has-prefix.ts#L10-L17\n * Checks if a given path starts with a given prefix. It ensures it matches\n * exactly without containing extra chars. e.g. prefix /docs should replace\n * for /docs, /docs/, /docs/a but not /docsss\n * @param path The path to check.\n * @param prefix The prefix to check against.\n */\nfunction pathHasPrefix(path: string, prefix: string): boolean {\n if (typeof path !== 'string') {\n return false\n }\n\n const {pathname} = parsePath(path)\n return pathname === prefix || pathname.startsWith(`${prefix}/`)\n}\n\n/**\n * From: https://github.com/vercel/next.js/blob/5469e6427b54ab7e9876d4c85b47f9c3afdc5c1f/packages/next/src/shared/lib/router/utils/parse-path.ts#L6-L22\n * Given a path this function will find the pathname, query and hash and return\n * them. This is useful to parse full paths on the client side.\n * @param path A path to parse e.g. /foo/bar?id=1#hash\n */\nfunction parsePath(path: string): {\n pathname: string\n query: string\n hash: string\n} {\n const hashIndex = path.indexOf('#')\n const queryIndex = path.indexOf('?')\n const hasQuery = queryIndex > -1 && (hashIndex < 0 || queryIndex < hashIndex)\n\n if (hasQuery || hashIndex > -1) {\n return {\n pathname: path.substring(0, hasQuery ? queryIndex : hashIndex),\n query: hasQuery ? path.substring(queryIndex, hashIndex > -1 ? hashIndex : undefined) : '',\n hash: hashIndex > -1 ? path.slice(hashIndex) : '',\n }\n }\n\n return {pathname: path, query: '', hash: ''}\n}\n\n/**\n * From: https://github.com/vercel/next.js/blob/5469e6427b54ab7e9876d4c85b47f9c3afdc5c1f/packages/next/src/shared/lib/router/utils/add-path-prefix.ts#L3C1-L14C2\n * Adds the provided prefix to the given path. It first ensures that the path\n * is indeed starting with a slash.\n */\nexport function addPathPrefix(path: string, prefix?: string): string {\n if (!path.startsWith('/') || !prefix) {\n return path\n }\n // If the path is exactly '/' then return just the prefix\n if (path === '/' && prefix) {\n return prefix\n }\n\n const {pathname, query, hash} = parsePath(path)\n return `${prefix}${pathname}${query}${hash}`\n}\n\n/**\n * From: https://github.com/vercel/next.js/blob/5469e6427b54ab7e9876d4c85b47f9c3afdc5c1f/packages/next/src/shared/lib/router/utils/remove-path-prefix.ts#L3-L39\n * Given a path and a prefix it will remove the prefix when it exists in the\n * given path. It ensures it matches exactly without containing extra chars\n * and if the prefix is not there it will be noop.\n *\n * @param path The path to remove the prefix from.\n * @param prefix The prefix to be removed.\n */\nexport function removePathPrefix(path: string, prefix: string): string {\n // If the path doesn't start with the prefix we can return it as is. This\n // protects us from situations where the prefix is a substring of the path\n // prefix such as:\n //\n // For prefix: /blog\n //\n // /blog -> true\n // /blog/ -> true\n // /blog/1 -> true\n // /blogging -> false\n // /blogging/ -> false\n // /blogging/1 -> false\n if (!pathHasPrefix(path, prefix)) {\n return path\n }\n\n // Remove the prefix from the path via slicing.\n const withoutPrefix = path.slice(prefix.length)\n\n // If the path without the prefix starts with a `/` we can return it as is.\n if (withoutPrefix.startsWith('/')) {\n return withoutPrefix\n }\n\n // If the path without the prefix doesn't start with a `/` we need to add it\n // back to the path to make sure it's a valid path.\n return `/${withoutPrefix}`\n}\n\n/**\n * From: https://github.com/vercel/next.js/blob/dfe7fc03e2268e7cb765dce6a89e02c831c922d5/packages/next/src/client/normalize-trailing-slash.ts#L16\n * Normalizes the trailing slash of a path according to the `trailingSlash` option\n * in `next.config.js`.\n */\nexport const normalizePathTrailingSlash = (path: string, trailingSlash: boolean): string => {\n const {pathname, query, hash} = parsePath(path)\n if (trailingSlash) {\n if (pathname.endsWith('/')) {\n return `${pathname}${query}${hash}`\n }\n return `${pathname}/${query}${hash}`\n }\n\n return `${removeTrailingSlash(pathname)}${query}${hash}`\n}\n\n/**\n * From: https://github.com/vercel/next.js/blob/dfe7fc03e2268e7cb765dce6a89e02c831c922d5/packages/next/src/shared/lib/router/utils/remove-trailing-slash.ts#L8\n * Removes the trailing slash for a given route or page path. Preserves the\n * root page. Examples:\n * - `/foo/bar/` -> `/foo/bar`\n * - `/foo/bar` -> `/foo/bar`\n * - `/` -> `/`\n */\nfunction removeTrailingSlash(route: string) {\n return route.replace(/\\/$/, '') || '/'\n}\n","import {\n type HistoryAdapter,\n type HistoryAdapterNavigate,\n type HistoryRefresh,\n VisualEditing as VisualEditingComponent,\n type VisualEditingOptions,\n} from '@sanity/visual-editing/react'\nimport {usePathname, useRouter, useSearchParams} from 'next/navigation'\nimport {revalidateRootLayout} from 'next-sanity/visual-editing/server-actions'\nimport {useCallback, useEffect, useMemo, useRef, useState} from 'react'\n\nimport {addPathPrefix, normalizePathTrailingSlash, removePathPrefix} from './utils'\n\n/**\n * @public\n */\nexport interface VisualEditingProps extends Omit<VisualEditingOptions, 'history'> {\n /**\n * @deprecated The histoy adapter is already implemented\n */\n history?: never\n /**\n * If next.config.ts is configured with a basePath we try to configure it automatically,\n * you can disable this by setting basePath to ''.\n * @example basePath=\"/my-custom-base-path\"\n * @alpha experimental and may change without notice\n * @defaultValue process.env.__NEXT_ROUTER_BASEPATH || ''\n */\n basePath?: string\n /**\n * If next.config.ts is configured with a `trailingSlash` we try to detect it automatically,\n * it can be controlled manually by passing a boolean.\n * @example trailingSlash={true}\n * @alpha experimental and may change without notice\n * @defaultValue Boolean(process.env.__NEXT_TRAILING_SLASH)\n */\n trailingSlash?: boolean\n}\n\nexport default function VisualEditing(props: VisualEditingProps): React.JSX.Element | null {\n const {basePath = '', plugins, components, refresh, trailingSlash = false, zIndex} = props\n\n const router = useRouter()\n const routerRef = useRef(router)\n const [navigate, setNavigate] = useState<HistoryAdapterNavigate | undefined>()\n\n useEffect(() => {\n routerRef.current = router\n }, [router])\n\n const history = useMemo<HistoryAdapter>(\n () => ({\n subscribe: (_navigate) => {\n setNavigate(() => _navigate)\n return () => setNavigate(undefined)\n },\n update: (update) => {\n switch (update.type) {\n case 'push':\n return routerRef.current.push(removePathPrefix(update.url, basePath))\n case 'pop':\n return routerRef.current.back()\n case 'replace':\n return routerRef.current.replace(removePathPrefix(update.url, basePath))\n default:\n throw new Error(`Unknown update type`, {cause: update})\n }\n },\n }),\n [basePath],\n )\n\n const pathname = usePathname()\n const searchParams = useSearchParams()\n useEffect(() => {\n if (navigate) {\n navigate({\n type: 'push',\n url: normalizePathTrailingSlash(\n addPathPrefix(\n `${pathname}${searchParams?.size ? `?${searchParams.toString()}` : ''}`,\n basePath,\n ),\n trailingSlash,\n ),\n })\n }\n }, [basePath, navigate, pathname, searchParams, trailingSlash])\n\n const handleRefresh = useCallback(\n (payload: HistoryRefresh) => {\n if (refresh) return refresh(payload)\n\n const manualFastRefresh = () => {\n // eslint-disable-next-line no-console\n console.debug(\n 'Live preview is setup, calling router.refresh() to refresh the server components without refetching cached data',\n )\n routerRef.current.refresh()\n return Promise.resolve()\n }\n const manualFallbackRefresh = () => {\n // eslint-disable-next-line no-console\n console.debug(\n 'No loaders in live mode detected, or preview kit setup, revalidating root layout',\n )\n return revalidateRootLayout()\n }\n\n const mutationFallbackRefresh = () => {\n // eslint-disable-next-line no-console\n console.debug(\n 'No loaders in live mode detected, or preview kit setup, revalidating root layout',\n )\n return revalidateRootLayout()\n }\n\n switch (payload.source) {\n case 'manual':\n return payload.livePreviewEnabled ? manualFastRefresh() : manualFallbackRefresh()\n case 'mutation':\n return payload.livePreviewEnabled ? mutationFastRefresh() : mutationFallbackRefresh()\n default:\n throw new Error('Unknown refresh source', {cause: payload})\n }\n },\n [refresh],\n )\n\n return (\n <VisualEditingComponent\n plugins={plugins}\n components={components}\n history={history}\n portal\n refresh={handleRefresh}\n zIndex={zIndex}\n />\n )\n}\n\nfunction mutationFastRefresh(): false {\n // eslint-disable-next-line no-console\n console.debug(\n 'Live preview is setup, mutation is skipped assuming its handled by the live preview',\n )\n return false\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VisualEditing.js","names":["VisualEditingComponent"],"sources":["../src/visual-editing/client-component/utils.ts","../src/visual-editing/client-component/VisualEditing.tsx"],"sourcesContent":["/**\n * From: https://github.com/vercel/next.js/blob/5469e6427b54ab7e9876d4c85b47f9c3afdc5c1f/packages/next/src/shared/lib/router/utils/path-has-prefix.ts#L10-L17\n * Checks if a given path starts with a given prefix. It ensures it matches\n * exactly without containing extra chars. e.g. prefix /docs should replace\n * for /docs, /docs/, /docs/a but not /docsss\n * @param path The path to check.\n * @param prefix The prefix to check against.\n */\nfunction pathHasPrefix(path: string, prefix: string): boolean {\n if (typeof path !== 'string') {\n return false\n }\n\n const {pathname} = parsePath(path)\n return pathname === prefix || pathname.startsWith(`${prefix}/`)\n}\n\n/**\n * From: https://github.com/vercel/next.js/blob/5469e6427b54ab7e9876d4c85b47f9c3afdc5c1f/packages/next/src/shared/lib/router/utils/parse-path.ts#L6-L22\n * Given a path this function will find the pathname, query and hash and return\n * them. This is useful to parse full paths on the client side.\n * @param path A path to parse e.g. /foo/bar?id=1#hash\n */\nfunction parsePath(path: string): {\n pathname: string\n query: string\n hash: string\n} {\n const hashIndex = path.indexOf('#')\n const queryIndex = path.indexOf('?')\n const hasQuery = queryIndex > -1 && (hashIndex < 0 || queryIndex < hashIndex)\n\n if (hasQuery || hashIndex > -1) {\n return {\n pathname: path.substring(0, hasQuery ? queryIndex : hashIndex),\n query: hasQuery ? path.substring(queryIndex, hashIndex > -1 ? hashIndex : undefined) : '',\n hash: hashIndex > -1 ? path.slice(hashIndex) : '',\n }\n }\n\n return {pathname: path, query: '', hash: ''}\n}\n\n/**\n * From: https://github.com/vercel/next.js/blob/5469e6427b54ab7e9876d4c85b47f9c3afdc5c1f/packages/next/src/shared/lib/router/utils/add-path-prefix.ts#L3C1-L14C2\n * Adds the provided prefix to the given path. It first ensures that the path\n * is indeed starting with a slash.\n */\nexport function addPathPrefix(path: string, prefix?: string): string {\n if (!path.startsWith('/') || !prefix) {\n return path\n }\n // If the path is exactly '/' then return just the prefix\n if (path === '/' && prefix) {\n return prefix\n }\n\n const {pathname, query, hash} = parsePath(path)\n return `${prefix}${pathname}${query}${hash}`\n}\n\n/**\n * From: https://github.com/vercel/next.js/blob/5469e6427b54ab7e9876d4c85b47f9c3afdc5c1f/packages/next/src/shared/lib/router/utils/remove-path-prefix.ts#L3-L39\n * Given a path and a prefix it will remove the prefix when it exists in the\n * given path. It ensures it matches exactly without containing extra chars\n * and if the prefix is not there it will be noop.\n *\n * @param path The path to remove the prefix from.\n * @param prefix The prefix to be removed.\n */\nexport function removePathPrefix(path: string, prefix: string): string {\n // If the path doesn't start with the prefix we can return it as is. This\n // protects us from situations where the prefix is a substring of the path\n // prefix such as:\n //\n // For prefix: /blog\n //\n // /blog -> true\n // /blog/ -> true\n // /blog/1 -> true\n // /blogging -> false\n // /blogging/ -> false\n // /blogging/1 -> false\n if (!pathHasPrefix(path, prefix)) {\n return path\n }\n\n // Remove the prefix from the path via slicing.\n const withoutPrefix = path.slice(prefix.length)\n\n // If the path without the prefix starts with a `/` we can return it as is.\n if (withoutPrefix.startsWith('/')) {\n return withoutPrefix\n }\n\n // If the path without the prefix doesn't start with a `/` we need to add it\n // back to the path to make sure it's a valid path.\n return `/${withoutPrefix}`\n}\n\n/**\n * From: https://github.com/vercel/next.js/blob/dfe7fc03e2268e7cb765dce6a89e02c831c922d5/packages/next/src/client/normalize-trailing-slash.ts#L16\n * Normalizes the trailing slash of a path according to the `trailingSlash` option\n * in `next.config.js`.\n */\nexport const normalizePathTrailingSlash = (path: string, trailingSlash: boolean): string => {\n const {pathname, query, hash} = parsePath(path)\n if (trailingSlash) {\n if (pathname.endsWith('/')) {\n return `${pathname}${query}${hash}`\n }\n return `${pathname}/${query}${hash}`\n }\n\n return `${removeTrailingSlash(pathname)}${query}${hash}`\n}\n\n/**\n * From: https://github.com/vercel/next.js/blob/dfe7fc03e2268e7cb765dce6a89e02c831c922d5/packages/next/src/shared/lib/router/utils/remove-trailing-slash.ts#L8\n * Removes the trailing slash for a given route or page path. Preserves the\n * root page. Examples:\n * - `/foo/bar/` -> `/foo/bar`\n * - `/foo/bar` -> `/foo/bar`\n * - `/` -> `/`\n */\nfunction removeTrailingSlash(route: string) {\n return route.replace(/\\/$/, '') || '/'\n}\n","import {\n type HistoryAdapter,\n type HistoryAdapterNavigate,\n type HistoryRefresh,\n VisualEditing as VisualEditingComponent,\n type VisualEditingOptions,\n} from '@sanity/visual-editing/react'\nimport {revalidateRootLayout} from 'next-sanity/visual-editing/server-actions'\nimport {usePathname, useRouter, useSearchParams} from 'next/navigation'\nimport {useCallback, useEffect, useMemo, useRef, useState} from 'react'\n\nimport {addPathPrefix, normalizePathTrailingSlash, removePathPrefix} from './utils'\n\n/**\n * @public\n */\nexport interface VisualEditingProps extends Omit<VisualEditingOptions, 'history'> {\n /**\n * @deprecated The histoy adapter is already implemented\n */\n history?: never\n /**\n * If next.config.ts is configured with a basePath we try to configure it automatically,\n * you can disable this by setting basePath to ''.\n * @example basePath=\"/my-custom-base-path\"\n * @alpha experimental and may change without notice\n * @defaultValue process.env.__NEXT_ROUTER_BASEPATH || ''\n */\n basePath?: string\n /**\n * If next.config.ts is configured with a `trailingSlash` we try to detect it automatically,\n * it can be controlled manually by passing a boolean.\n * @example trailingSlash={true}\n * @alpha experimental and may change without notice\n * @defaultValue Boolean(process.env.__NEXT_TRAILING_SLASH)\n */\n trailingSlash?: boolean\n}\n\nexport default function VisualEditing(props: VisualEditingProps): React.JSX.Element | null {\n const {basePath = '', plugins, components, refresh, trailingSlash = false, zIndex} = props\n\n const router = useRouter()\n const routerRef = useRef(router)\n const [navigate, setNavigate] = useState<HistoryAdapterNavigate | undefined>()\n\n useEffect(() => {\n routerRef.current = router\n }, [router])\n\n const history = useMemo<HistoryAdapter>(\n () => ({\n subscribe: (_navigate) => {\n setNavigate(() => _navigate)\n return () => setNavigate(undefined)\n },\n update: (update) => {\n switch (update.type) {\n case 'push':\n return routerRef.current.push(removePathPrefix(update.url, basePath))\n case 'pop':\n return routerRef.current.back()\n case 'replace':\n return routerRef.current.replace(removePathPrefix(update.url, basePath))\n default:\n throw new Error(`Unknown update type`, {cause: update})\n }\n },\n }),\n [basePath],\n )\n\n const pathname = usePathname()\n const searchParams = useSearchParams()\n useEffect(() => {\n if (navigate) {\n navigate({\n type: 'push',\n url: normalizePathTrailingSlash(\n addPathPrefix(\n `${pathname}${searchParams?.size ? `?${searchParams.toString()}` : ''}`,\n basePath,\n ),\n trailingSlash,\n ),\n })\n }\n }, [basePath, navigate, pathname, searchParams, trailingSlash])\n\n const handleRefresh = useCallback(\n (payload: HistoryRefresh) => {\n if (refresh) return refresh(payload)\n\n const manualFastRefresh = () => {\n // oxlint-disable-next-line no-console\n console.debug(\n 'Live preview is setup, calling router.refresh() to refresh the server components without refetching cached data',\n )\n routerRef.current.refresh()\n return Promise.resolve()\n }\n const manualFallbackRefresh = () => {\n // oxlint-disable-next-line no-console\n console.debug(\n 'No loaders in live mode detected, or preview kit setup, revalidating root layout',\n )\n return revalidateRootLayout()\n }\n\n const mutationFallbackRefresh = () => {\n // oxlint-disable-next-line no-console\n console.debug(\n 'No loaders in live mode detected, or preview kit setup, revalidating root layout',\n )\n return revalidateRootLayout()\n }\n\n switch (payload.source) {\n case 'manual':\n return payload.livePreviewEnabled ? manualFastRefresh() : manualFallbackRefresh()\n case 'mutation':\n return payload.livePreviewEnabled ? mutationFastRefresh() : mutationFallbackRefresh()\n default:\n throw new Error('Unknown refresh source', {cause: payload})\n }\n },\n [refresh],\n )\n\n return (\n <VisualEditingComponent\n plugins={plugins}\n components={components}\n history={history}\n portal\n refresh={handleRefresh}\n zIndex={zIndex}\n />\n )\n}\n\nfunction mutationFastRefresh(): false {\n // oxlint-disable-next-line no-console\n console.debug(\n 'Live preview is setup, mutation is skipped assuming its handled by the live preview',\n )\n return false\n}\n"],"mappings":"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"}
|
package/dist/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","names":["perspectiveListeners: Set<() => void>","perspective: DraftPerspective","environmentListeners: Set<() => void>","environment: DraftEnvironment","comlinkListeners: Set<() => void>","comlink: Node<LoaderNodeMsg, LoaderControllerMsg> | null","comlinkProjectId: string | null","comlinkDataset: string | null"],"sources":["../src/live/hooks/context.ts"],"sourcesContent":["import type {ClientPerspective} from '@sanity/client'\nimport {type Node} from '@sanity/comlink'\nimport {type LoaderControllerMsg, type LoaderNodeMsg} from '@sanity/presentation-comlink'\n\n/**\n * The Sanity Client perspective used when fetching data in Draft Mode, in the `sanityFetch` calls\n * used by React Server Components on the page. Note that some of them might set the `perspective` to a different value.\n * This value is what's used by default.\n * @public\n */\nexport type DraftPerspective = 'checking' | 'unknown' | ClientPerspective\n\n/** @internal */\nexport const perspectiveListeners: Set<() => void> = new Set()\n/** @internal */\nexport let perspective: DraftPerspective = 'checking'\n/** @internal */\nexport function setPerspective(nextPerspective: DraftPerspective): void {\n if (perspective.toString() === nextPerspective.toString()) return\n perspective = nextPerspective\n for (const onPerspectiveChange of perspectiveListeners) {\n onPerspectiveChange()\n }\n}\n\n/**\n *\n * @public\n */\nexport type DraftEnvironment =\n | 'checking'\n | 'presentation-iframe'\n | 'presentation-window'\n | 'live'\n | 'static'\n | 'unknown'\n\n/** @internal */\nexport const environmentListeners: Set<() => void> = new Set()\n/** @internal */\nexport let environment: DraftEnvironment = 'checking'\n/** @internal */\nexport function setEnvironment(nextEnvironment: DraftEnvironment): void {\n environment = nextEnvironment\n for (const onEnvironmentChange of environmentListeners) {\n onEnvironmentChange()\n }\n}\n\n/** @internal */\nexport const comlinkListeners: Set<() => void> = new Set()\n/** @internal */\nexport let comlink: Node<LoaderNodeMsg, LoaderControllerMsg> | null = null\n/** @internal */\nexport let comlinkProjectId: string | null = null\n/** @internal */\nexport let comlinkDataset: string | null = null\n/** @internal */\nexport function setComlink(nextComlink: Node<LoaderNodeMsg, LoaderControllerMsg> | null): void {\n comlink = nextComlink\n for (const onComlinkChange of comlinkListeners) {\n onComlinkChange()\n }\n}\n/** @internal */\nexport function setComlinkClientConfig(\n nextComlinkProjectId: string | null,\n nextComlinkDataset: string | null,\n): void {\n comlinkProjectId = nextComlinkProjectId\n comlinkDataset = nextComlinkDataset\n for (const onComlinkChange of comlinkListeners) {\n onComlinkChange()\n }\n}\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"context.js","names":["perspectiveListeners: Set<() => void>","perspective: DraftPerspective","environmentListeners: Set<() => void>","environment: DraftEnvironment","comlinkListeners: Set<() => void>","comlink: Node<LoaderNodeMsg, LoaderControllerMsg> | null","comlinkProjectId: string | null","comlinkDataset: string | null"],"sources":["../src/live/hooks/context.ts"],"sourcesContent":["import type {ClientPerspective} from '@sanity/client'\n\nimport {type Node} from '@sanity/comlink'\nimport {type LoaderControllerMsg, type LoaderNodeMsg} from '@sanity/presentation-comlink'\n\n/**\n * The Sanity Client perspective used when fetching data in Draft Mode, in the `sanityFetch` calls\n * used by React Server Components on the page. Note that some of them might set the `perspective` to a different value.\n * This value is what's used by default.\n * @public\n */\nexport type DraftPerspective = 'checking' | 'unknown' | ClientPerspective\n\n/** @internal */\nexport const perspectiveListeners: Set<() => void> = new Set()\n/** @internal */\nexport let perspective: DraftPerspective = 'checking'\n/** @internal */\nexport function setPerspective(nextPerspective: DraftPerspective): void {\n if (perspective.toString() === nextPerspective.toString()) return\n perspective = nextPerspective\n for (const onPerspectiveChange of perspectiveListeners) {\n onPerspectiveChange()\n }\n}\n\n/**\n *\n * @public\n */\nexport type DraftEnvironment =\n | 'checking'\n | 'presentation-iframe'\n | 'presentation-window'\n | 'live'\n | 'static'\n | 'unknown'\n\n/** @internal */\nexport const environmentListeners: Set<() => void> = new Set()\n/** @internal */\nexport let environment: DraftEnvironment = 'checking'\n/** @internal */\nexport function setEnvironment(nextEnvironment: DraftEnvironment): void {\n environment = nextEnvironment\n for (const onEnvironmentChange of environmentListeners) {\n onEnvironmentChange()\n }\n}\n\n/** @internal */\nexport const comlinkListeners: Set<() => void> = new Set()\n/** @internal */\nexport let comlink: Node<LoaderNodeMsg, LoaderControllerMsg> | null = null\n/** @internal */\nexport let comlinkProjectId: string | null = null\n/** @internal */\nexport let comlinkDataset: string | null = null\n/** @internal */\nexport function setComlink(nextComlink: Node<LoaderNodeMsg, LoaderControllerMsg> | null): void {\n comlink = nextComlink\n for (const onComlinkChange of comlinkListeners) {\n onComlinkChange()\n }\n}\n/** @internal */\nexport function setComlinkClientConfig(\n nextComlinkProjectId: string | null,\n nextComlinkDataset: string | null,\n): void {\n comlinkProjectId = nextComlinkProjectId\n comlinkDataset = nextComlinkDataset\n for (const onComlinkChange of comlinkListeners) {\n onComlinkChange()\n }\n}\n"],"mappings":";;AAcA,MAAaA,uCAAwC,IAAI,KAAK;AAE9D,IAAWC,cAAgC;AAE3C,SAAgB,eAAe,iBAAyC;AACtE,KAAI,YAAY,UAAU,KAAK,gBAAgB,UAAU,CAAE;AAC3D,eAAc;AACd,MAAK,MAAM,uBAAuB,qBAChC,sBAAqB;;AAiBzB,MAAaC,uCAAwC,IAAI,KAAK;AAE9D,IAAWC,cAAgC;AAE3C,SAAgB,eAAe,iBAAyC;AACtE,eAAc;AACd,MAAK,MAAM,uBAAuB,qBAChC,sBAAqB;;AAKzB,MAAaC,mCAAoC,IAAI,KAAK;AAE1D,IAAWC,UAA2D;AAEtE,IAAWC,mBAAkC;AAE7C,IAAWC,iBAAgC;AAE3C,SAAgB,WAAW,aAAoE;AAC7F,WAAU;AACV,MAAK,MAAM,mBAAmB,iBAC5B,kBAAiB;;AAIrB,SAAgB,uBACd,sBACA,oBACM;AACN,oBAAmB;AACnB,kBAAiB;AACjB,MAAK,MAAM,mBAAmB,iBAC5B,kBAAiB"}
|
package/dist/defineLive.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defineLive.d.ts","names":[],"sources":["../src/live/defineLive.tsx"],"sourcesContent":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"defineLive.d.ts","names":[],"sources":["../src/live/defineLive.tsx"],"sourcesContent":[],"mappings":";;AAmBA;;AAEW,KAFC,sBAAA,GAED,CAAA,0BAAA,MAAA,CAAA,CAAA,OAAA,EAAA;EAAsB,KAAA,EADxB,WACwB;EAAR,MAAA,CAAA,EAAd,WAAc,GAAA,OAAA,CAAQ,WAAR,CAAA;EAQD;;;;;;EAYlB,IAAA,CAAA,EAAA,MAAA,EAAA;EASN,WAAY,CAAA,EArBI,OAqBJ,CArBY,iBAqBZ,EAAA,KAAA,CAAA;EACH,KAAA,CAAA,EAAA,OAAA;EACE;;;EAQa,GAAA,CAAA,EAAA,KAAA;EAAR;;;;;EAgBA,UAAA,CAAA,EAAA,MAAA;CAAR,EAAA,GAnCF,OAmCE,CAAA;EAGF,IAAA,EArCE,YAqCI,CArCS,WAqCT,CAAA;EAAA,SAAA,EApCC,gBAoCD,GAAA,IAAA;EAKZ,IAAiB,EAAA,MAAA,EAAA;CAoDI,CAAA;;;;AAYJ,KAlGL,2BAAA,GAsGF,CAAA,0BAAA,MAAA,CAAA,CAAA,KAAA,EAAA;EAmCV,KAAgB,EAxIP,WAwIO;EAAmB,MAAA,CAAA,EAvIxB,WAuIwB,GAvIV,OAuIU,CAvIF,WAuIE,CAAA;EAKpB;;;;;;;gBApIC,QAAQ;;;;;;;;;;;;;UAad,aAAa;eACR;;QAEP,QAAQ,QAAQ,KAAA,CAAM;MAGxB,KAAA,CAAM;;;;UAKK,sBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAoDI;;;;;8BAMS,cAAc;;;;;UAM3B,uBAAA;;;;UAIP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmCM,UAAA,SAAmB;;;;;eAKpB;;;;;cAKD,KAAA,CAAM,cAAc;;;;oBAId"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/draft-mode/define-enable-draft-mode.ts"],"sourcesContent":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/draft-mode/define-enable-draft-mode.ts"],"sourcesContent":[],"mappings":";;AAUA;AAcA;AACiB,UAfA,4BAAA,CAeA;EAAoB,MAAA,EAd3B,YAc2B;EAAR;;AAoB7B;;;;;;;;;UArBiB,eAAA;iBACA,YAAY,QAAQ;;;;;;;;;;;;;;;;;;;iBAoBrB,qBAAA,UAA+B,+BAA+B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/draft-mode/define-enable-draft-mode.ts"],"sourcesContent":["import type {SanityClient} from '@sanity/client'\nimport {validatePreviewUrl} from '@sanity/preview-url-secret'\nimport {perspectiveCookieName} from '@sanity/preview-url-secret/constants'\nimport {cookies, draftMode} from 'next/headers'\nimport {redirect} from 'next/navigation'\n\n/**\n * @public\n */\nexport interface DefineEnableDraftModeOptions {\n client: SanityClient\n /**\n * Force secure cookies in development mode.\n * Enable this when using Next.js --experimental-https flag.\n * This option has no effect in production (cookies are always secure).\n * @defaultValue false\n */\n secureDevMode?: boolean\n}\n\n/**\n * @public\n */\nexport interface EnableDraftMode {\n GET: (request: Request) => Promise<Response>\n}\n\n/**\n * Sets up an API route for enabling draft mode, can be paired with the `previewUrl.previewMode.enable` in `sanity/presentation`.\n * Can also be used with `sanity-plugin-iframe-pane`.\n * @example\n * ```ts\n * // src/app/api/draft-mode/enable/route.ts\n *\n * import { defineEnableDraftMode } from \"next-sanity/draft-mode\";\n * import { client } from \"@/sanity/lib/client\";\n *\n * export const { GET } = defineEnableDraftMode({\n * client: client.withConfig({ token: process.env.SANITY_API_READ_TOKEN }),\n * });\n * ```\n *\n * @public\n */\nexport function defineEnableDraftMode(options: DefineEnableDraftModeOptions): EnableDraftMode {\n const {client} = options\n return {\n GET: async (request: Request) => {\n //
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/draft-mode/define-enable-draft-mode.ts"],"sourcesContent":["import type {SanityClient} from '@sanity/client'\n\nimport {validatePreviewUrl} from '@sanity/preview-url-secret'\nimport {perspectiveCookieName} from '@sanity/preview-url-secret/constants'\nimport {cookies, draftMode} from 'next/headers'\nimport {redirect} from 'next/navigation'\n\n/**\n * @public\n */\nexport interface DefineEnableDraftModeOptions {\n client: SanityClient\n /**\n * Force secure cookies in development mode.\n * Enable this when using Next.js --experimental-https flag.\n * This option has no effect in production (cookies are always secure).\n * @defaultValue false\n */\n secureDevMode?: boolean\n}\n\n/**\n * @public\n */\nexport interface EnableDraftMode {\n GET: (request: Request) => Promise<Response>\n}\n\n/**\n * Sets up an API route for enabling draft mode, can be paired with the `previewUrl.previewMode.enable` in `sanity/presentation`.\n * Can also be used with `sanity-plugin-iframe-pane`.\n * @example\n * ```ts\n * // src/app/api/draft-mode/enable/route.ts\n *\n * import { defineEnableDraftMode } from \"next-sanity/draft-mode\";\n * import { client } from \"@/sanity/lib/client\";\n *\n * export const { GET } = defineEnableDraftMode({\n * client: client.withConfig({ token: process.env.SANITY_API_READ_TOKEN }),\n * });\n * ```\n *\n * @public\n */\nexport function defineEnableDraftMode(options: DefineEnableDraftModeOptions): EnableDraftMode {\n const {client} = options\n return {\n GET: async (request: Request) => {\n // @TODO check if already in draft mode at a much earlier stage, and skip validation\n\n const {\n isValid,\n redirectTo = '/',\n studioPreviewPerspective,\n } = await validatePreviewUrl(client, request.url)\n if (!isValid) {\n return new Response('Invalid secret', {status: 401})\n }\n\n const draftModeStore = await draftMode()\n\n // Let's enable draft mode if it's not already enabled\n if (!draftModeStore.isEnabled) {\n draftModeStore.enable()\n }\n\n const isProduction = process.env.NODE_ENV === 'production'\n\n // We can't auto-detect HTTPS in dev due to Next.js limitations,\n // so we need an explicit option\n const isSecure = isProduction || (options.secureDevMode ?? false)\n\n // Override cookie header for draft mode for usage in live-preview\n // https://github.com/vercel/next.js/issues/49927\n const cookieStore = await cookies()\n const cookie = cookieStore.get('__prerender_bypass')!\n cookieStore.set({\n name: '__prerender_bypass',\n value: cookie?.value,\n httpOnly: true,\n path: '/',\n secure: isSecure,\n sameSite: isSecure ? 'none' : 'lax',\n })\n\n if (studioPreviewPerspective) {\n cookieStore.set({\n name: perspectiveCookieName,\n value: studioPreviewPerspective,\n httpOnly: true,\n path: '/',\n secure: isSecure,\n sameSite: isSecure ? 'none' : 'lax',\n })\n }\n\n // the `redirect` function throws, and eventually returns a Promise<Response>. TSC doesn't \"see\" that so we have to tell it\n return redirect(redirectTo) as Promise<Response>\n },\n }\n}\n"],"mappings":";;;;AA6CA,SAAgB,sBAAsB,SAAwD;CAC5F,MAAM,EAAC,WAAU;AACjB,QAAO,EACL,KAAK,OAAO,YAAqB;EAG/B,MAAM,EACJ,SACA,aAAa,KACb,6BACE,MAAM,mBAAmB,QAAQ,QAAQ,IAAI;AACjD,MAAI,CAAC,QACH,QAAO,IAAI,SAAS,kBAAkB,EAAC,QAAQ,KAAI,CAAC;EAGtD,MAAM,iBAAiB,MAAM,WAAW;AAGxC,MAAI,CAAC,eAAe,UAClB,gBAAe,QAAQ;EAOzB,MAAM,WAJe,QAAQ,IAAI,aAAa,iBAIZ,QAAQ,iBAAiB;EAI3D,MAAM,cAAc,MAAM,SAAS;EACnC,MAAM,SAAS,YAAY,IAAI,qBAAqB;AACpD,cAAY,IAAI;GACd,MAAM;GACN,OAAO,QAAQ;GACf,UAAU;GACV,MAAM;GACN,QAAQ;GACR,UAAU,WAAW,SAAS;GAC/B,CAAC;AAEF,MAAI,yBACF,aAAY,IAAI;GACd,MAAM;GACN,OAAO;GACP,UAAU;GACV,MAAM;GACN,QAAQ;GACR,UAAU,WAAW,SAAS;GAC/B,CAAC;AAIJ,SAAO,SAAS,WAAW;IAE9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.d.ts","names":[],"sources":["../../../src/experimental/types.ts","../../../src/experimental/client-components/live.tsx"],"sourcesContent":[],"mappings":";;UAEiB,kBAAA,SAA2B,KAC1C;AADF;;
|
|
1
|
+
{"version":3,"file":"live.d.ts","names":[],"sources":["../../../src/experimental/types.ts","../../../src/experimental/client-components/live.tsx"],"sourcesContent":[],"mappings":";;UAEiB,kBAAA,SAA2B,KAC1C;AADF;;ACkCA;AACU,UADO,eAAA,CACP;EAYW,MAAA,EAZX,kBAYW;EAED,gBAAA,EAAA,OAAA;EAAmC,cAAA,CAAA,EAAA,OAAA;EAAwB,cAAA,CAAA,EAAA,OAAA;EACxE,kBAAA,CAAA,EAAA,OAAA;EACsC,UAAA,EAAA,MAAA,GAAA,SAAA;EAAR;;;;;;qBAJhB;uCAED,mCAAmC,wBAAwB,gBACxE;qCAC8B,QAAQ;;;;;iBAkCrB,UAAA,QAAkB,kBAAkB,KAAA,CAAM,GAAA,CAAI"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.js","names":[],"sources":["../../../src/experimental/client-components/live.tsx"],"sourcesContent":["'use client'\n\nimport {\n createClient,\n type ClientPerspective,\n type LiveEvent,\n type LiveEventGoAway,\n type SyncTag,\n} from '@sanity/client'\nimport {isMaybePresentation, isMaybePreviewWindow} from '@sanity/presentation-comlink'\nimport dynamic from 'next/dynamic'\nimport {useRouter} from 'next/navigation'\nimport {useEffect, useMemo, useRef, useState, useEffectEvent} from 'react'\nimport {setEnvironment, setPerspective} from '../../live/hooks/context'\nimport {isCorsOriginError} from '../../isCorsOriginError'\nimport type {SanityClientConfig} from '../types'\nimport {sanitizePerspective} from '../../live/utils'\nimport {PUBLISHED_SYNC_TAG_PREFIX, type DRAFT_SYNC_TAG_PREFIX} from '../constants'\n\nconst PresentationComlink = dynamic(() => import('./PresentationComlink'), {ssr: false})\nconst RefreshOnMount = dynamic(() => import('../../live/client-components/live/RefreshOnMount'), {\n ssr: false,\n})\nconst RefreshOnFocus = dynamic(() => import('../../live/client-components/live/RefreshOnFocus'), {\n ssr: false,\n})\nconst RefreshOnReconnect = dynamic(\n () => import('../../live/client-components/live/RefreshOnReconnect'),\n {ssr: false},\n)\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport interface SanityLiveProps {\n config: SanityClientConfig\n draftModeEnabled: boolean\n refreshOnMount?: boolean\n refreshOnFocus?: boolean\n refreshOnReconnect?: boolean\n requestTag: string | undefined\n /**\n * Handle errors from the Live Events subscription.\n * By default it's reported using `console.error`, you can override this prop to handle it in your own way.\n */\n onError?: (error: unknown) => void\n intervalOnGoAway?: number | false\n onGoAway?: (event: LiveEventGoAway, intervalOnGoAway: number | false) => void\n revalidateSyncTags: (\n tags: `${typeof PUBLISHED_SYNC_TAG_PREFIX | typeof DRAFT_SYNC_TAG_PREFIX}${SyncTag}`[],\n ) => Promise<void | 'refresh'>\n resolveDraftModePerspective: () => Promise<ClientPerspective>\n}\n\nfunction handleError(error: unknown) {\n /* eslint-disable no-console */\n if (isCorsOriginError(error)) {\n console.warn(\n `Sanity Live is unable to connect to the Sanity API as the current origin - ${window.origin} - is not in the list of allowed CORS origins for this Sanity Project.`,\n error.addOriginUrl && `Add it here:`,\n error.addOriginUrl?.toString(),\n )\n } else {\n console.error(error)\n }\n /* eslint-enable no-console */\n}\n\nfunction handleOnGoAway(event: LiveEventGoAway, intervalOnGoAway: number | false) {\n /* eslint-disable no-console */\n if (intervalOnGoAway) {\n console.warn(\n 'Sanity Live connection closed, switching to long polling set to a interval of',\n intervalOnGoAway / 1000,\n 'seconds and the server gave this reason:',\n event.reason,\n )\n } else {\n console.error(\n 'Sanity Live connection closed, automatic revalidation is disabled, the server gave this reason:',\n event.reason,\n )\n }\n /* eslint-enable no-console */\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport default function SanityLive(props: SanityLiveProps): React.JSX.Element | null {\n const {\n config,\n draftModeEnabled,\n refreshOnMount = false,\n refreshOnFocus = draftModeEnabled\n ? false\n : typeof window === 'undefined'\n ? true\n : window.self === window.top,\n refreshOnReconnect = true,\n intervalOnGoAway = 30_000,\n requestTag = 'next-loader.live',\n onError = handleError,\n onGoAway = handleOnGoAway,\n revalidateSyncTags,\n resolveDraftModePerspective,\n } = props\n const {projectId, dataset, apiHost, apiVersion, useProjectHostname, token, requestTagPrefix} =\n config\n\n const client = useMemo(\n () =>\n createClient({\n projectId,\n dataset,\n apiHost,\n apiVersion,\n useProjectHostname,\n ignoreBrowserTokenWarning: true,\n token,\n useCdn: false,\n requestTagPrefix,\n }),\n [apiHost, apiVersion, dataset, projectId, requestTagPrefix, token, useProjectHostname],\n )\n const [longPollingInterval, setLongPollingInterval] = useState<number | false>(false)\n const [resolvedInitialPerspective, setResolvedInitialPerspective] = useState(false)\n\n /**\n * 1. Handle Live Events and call revalidateTag or router.refresh when needed\n */\n const router = useRouter()\n const handleLiveEvent = useEffectEvent((event: LiveEvent) => {\n if (process.env.NODE_ENV !== 'production' && event.type === 'welcome') {\n // eslint-disable-next-line no-console\n console.info(\n 'Sanity is live with',\n token\n ? 'automatic revalidation for draft content changes as well as published content'\n : draftModeEnabled\n ? 'automatic revalidation for only published content. Provide a `browserToken` to `defineLive` to support draft content outside of Presentation Tool.'\n : 'automatic revalidation of published content',\n )\n // Disable long polling when welcome event is received, this is a no-op if long polling is already disabled\n setLongPollingInterval(false)\n } else if (event.type === 'message') {\n void revalidateSyncTags(\n event.tags.map((tag: SyncTag) => `${PUBLISHED_SYNC_TAG_PREFIX}${tag}` as const),\n ).then((result) => {\n if (result === 'refresh') router.refresh()\n })\n } else if (event.type === 'restart' || event.type === 'reconnect') {\n router.refresh()\n } else if (event.type === 'goaway') {\n onGoAway(event, intervalOnGoAway)\n setLongPollingInterval(intervalOnGoAway)\n }\n })\n // @TODO previous version that handle both published and draft events\n // useEffect(() => {\n // const subscription = client.live.events({includeDrafts: !!token, tag: requestTag}).subscribe({\n // next: handleLiveEvent,\n // error: (err: unknown) => {\n // onError(err)\n // },\n // })\n // return () => subscription.unsubscribe()\n // }, [client.live, onError, requestTag, token])\n useEffect(() => {\n const subscription = client.live.events({tag: requestTag}).subscribe({\n next: handleLiveEvent,\n error: (err: unknown) => {\n onError(err)\n },\n })\n return () => subscription.unsubscribe()\n }, [client.live, onError, requestTag, token])\n\n /**\n * Handle live events for drafts differently, only use it to trigger refreshes, don't expire the cache\n */\n const handleLiveDraftEvent = useEffectEvent((event: LiveEvent) => {\n if (event.type === 'message') {\n // Just refresh, due to cache bypass in draft mode it'll fetch fresh content (though we wish cache worked as in production)\n // @TODO if draft content is published, then this extra refresh is unnecessary, it's tricky to check since `event.id` are different on the two EventSource connections\n router.refresh()\n }\n })\n useEffect(() => {\n if (!token) return\n const subscription = client.live.events({includeDrafts: !!token, tag: requestTag}).subscribe({\n next: handleLiveDraftEvent,\n error: (err: unknown) => {\n onError(err)\n },\n })\n return () => subscription.unsubscribe()\n }, [client.live, onError, requestTag, token])\n\n /**\n * 2. Notify what perspective we're in, when in Draft Mode\n */\n useEffect(() => {\n if (resolvedInitialPerspective) return undefined\n\n if (!draftModeEnabled) {\n setResolvedInitialPerspective(true)\n setPerspective('unknown')\n return undefined\n }\n\n const controller = new AbortController()\n resolveDraftModePerspective()\n .then((perspective) => {\n if (controller.signal.aborted) return\n setResolvedInitialPerspective(true)\n setPerspective(sanitizePerspective(perspective, 'drafts'))\n })\n .catch((err) => {\n if (controller.signal.aborted) return\n console.error('Failed to resolve draft mode perspective', err)\n setResolvedInitialPerspective(true)\n setPerspective('unknown')\n })\n return () => controller.abort()\n }, [draftModeEnabled, resolveDraftModePerspective, resolvedInitialPerspective])\n\n const [loadComlink, setLoadComlink] = useState(false)\n /**\n * 3. Notify what environment we're in, when in Draft Mode\n */\n useEffect(() => {\n // If we might be in Presentation Tool, then skip detecting here as it's handled later\n if (isMaybePresentation()) return\n\n // If we're definitely not in Presentation Tool, then we can set the environment as stand-alone live preview\n // if we have both a browser token, and draft mode is enabled\n if (draftModeEnabled && token) {\n setEnvironment('live')\n return\n }\n // If we're in draft mode, but don't have a browser token, then we're in static mode\n // which means that published content is still live, but draft changes likely need manual refresh\n if (draftModeEnabled) {\n setEnvironment('static')\n return\n }\n\n // Fallback to `unknown` otherwise, as we simply don't know how it's setup\n setEnvironment('unknown')\n return\n }, [draftModeEnabled, token])\n\n /**\n * 4. If Presentation Tool is detected, load up the comlink and integrate with it\n */\n useEffect(() => {\n if (!isMaybePresentation()) return\n const controller = new AbortController()\n // Wait for a while to see if Presentation Tool is detected, before assuming the env to be stand-alone live preview\n const timeout = setTimeout(() => setEnvironment('live'), 3_000)\n window.addEventListener(\n 'message',\n ({data}: MessageEvent<unknown>) => {\n if (\n data &&\n typeof data === 'object' &&\n 'domain' in data &&\n data.domain === 'sanity/channels' &&\n 'from' in data &&\n data.from === 'presentation'\n ) {\n clearTimeout(timeout)\n setEnvironment(isMaybePreviewWindow() ? 'presentation-window' : 'presentation-iframe')\n setLoadComlink(true)\n controller.abort()\n }\n },\n {signal: controller.signal},\n )\n return () => {\n clearTimeout(timeout)\n controller.abort()\n }\n }, [])\n\n /**\n * 5. Warn if draft mode is being disabled\n * @TODO move logic into PresentationComlink, or maybe VisualEditing?\n */\n const draftModeEnabledWarnRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n useEffect(() => {\n if (!draftModeEnabled) return\n clearTimeout(draftModeEnabledWarnRef.current)\n return () => {\n draftModeEnabledWarnRef.current = setTimeout(() => {\n // eslint-disable-next-line no-console\n console.warn('Sanity Live: Draft mode was enabled, but is now being disabled')\n })\n }\n }, [draftModeEnabled])\n\n /**\n * 6. Handle switching to long polling when needed\n */\n useEffect(() => {\n if (!longPollingInterval) return\n const interval = setInterval(() => router.refresh(), longPollingInterval)\n return () => clearInterval(interval)\n }, [longPollingInterval, router])\n\n return (\n <>\n {draftModeEnabled && loadComlink && resolvedInitialPerspective && (\n <PresentationComlink\n projectId={projectId!}\n dataset={dataset!}\n draftModeEnabled={draftModeEnabled}\n />\n )}\n {!draftModeEnabled && refreshOnMount && <RefreshOnMount />}\n {!draftModeEnabled && refreshOnFocus && <RefreshOnFocus />}\n {!draftModeEnabled && refreshOnReconnect && <RefreshOnReconnect />}\n </>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"live.js","names":[],"sources":["../../../src/experimental/client-components/live.tsx"],"sourcesContent":["'use client'\n\nimport {\n createClient,\n type ClientPerspective,\n type LiveEvent,\n type LiveEventGoAway,\n type SyncTag,\n} from '@sanity/client'\nimport {isMaybePresentation, isMaybePreviewWindow} from '@sanity/presentation-comlink'\nimport dynamic from 'next/dynamic'\nimport {useRouter} from 'next/navigation'\nimport {useEffect, useMemo, useRef, useState, useEffectEvent} from 'react'\n\nimport type {SanityClientConfig} from '../types'\n\nimport {isCorsOriginError} from '../../isCorsOriginError'\nimport {setEnvironment, setPerspective} from '../../live/hooks/context'\nimport {sanitizePerspective} from '../../live/utils'\nimport {PUBLISHED_SYNC_TAG_PREFIX, type DRAFT_SYNC_TAG_PREFIX} from '../constants'\n\nconst PresentationComlink = dynamic(() => import('./PresentationComlink'), {ssr: false})\nconst RefreshOnMount = dynamic(() => import('../../live/client-components/live/RefreshOnMount'), {\n ssr: false,\n})\nconst RefreshOnFocus = dynamic(() => import('../../live/client-components/live/RefreshOnFocus'), {\n ssr: false,\n})\nconst RefreshOnReconnect = dynamic(\n () => import('../../live/client-components/live/RefreshOnReconnect'),\n {ssr: false},\n)\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport interface SanityLiveProps {\n config: SanityClientConfig\n draftModeEnabled: boolean\n refreshOnMount?: boolean\n refreshOnFocus?: boolean\n refreshOnReconnect?: boolean\n requestTag: string | undefined\n /**\n * Handle errors from the Live Events subscription.\n * By default it's reported using `console.error`, you can override this prop to handle it in your own way.\n */\n onError?: (error: unknown) => void\n intervalOnGoAway?: number | false\n onGoAway?: (event: LiveEventGoAway, intervalOnGoAway: number | false) => void\n revalidateSyncTags: (\n tags: `${typeof PUBLISHED_SYNC_TAG_PREFIX | typeof DRAFT_SYNC_TAG_PREFIX}${SyncTag}`[],\n ) => Promise<void | 'refresh'>\n resolveDraftModePerspective: () => Promise<ClientPerspective>\n}\n\nfunction handleError(error: unknown) {\n if (isCorsOriginError(error)) {\n console.warn(\n `Sanity Live is unable to connect to the Sanity API as the current origin - ${window.origin} - is not in the list of allowed CORS origins for this Sanity Project.`,\n error.addOriginUrl && `Add it here:`,\n error.addOriginUrl?.toString(),\n )\n } else {\n console.error(error)\n }\n}\n\nfunction handleOnGoAway(event: LiveEventGoAway, intervalOnGoAway: number | false) {\n if (intervalOnGoAway) {\n console.warn(\n 'Sanity Live connection closed, switching to long polling set to a interval of',\n intervalOnGoAway / 1000,\n 'seconds and the server gave this reason:',\n event.reason,\n )\n } else {\n console.error(\n 'Sanity Live connection closed, automatic revalidation is disabled, the server gave this reason:',\n event.reason,\n )\n }\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport default function SanityLive(props: SanityLiveProps): React.JSX.Element | null {\n const {\n config,\n draftModeEnabled,\n refreshOnMount = false,\n refreshOnFocus = draftModeEnabled\n ? false\n : typeof window === 'undefined'\n ? true\n : window.self === window.top,\n refreshOnReconnect = true,\n intervalOnGoAway = 30_000,\n requestTag = 'next-loader.live',\n onError = handleError,\n onGoAway = handleOnGoAway,\n revalidateSyncTags,\n resolveDraftModePerspective,\n } = props\n const {projectId, dataset, apiHost, apiVersion, useProjectHostname, token, requestTagPrefix} =\n config\n\n const client = useMemo(\n () =>\n createClient({\n projectId,\n dataset,\n apiHost,\n apiVersion,\n useProjectHostname,\n ignoreBrowserTokenWarning: true,\n token,\n useCdn: false,\n requestTagPrefix,\n }),\n [apiHost, apiVersion, dataset, projectId, requestTagPrefix, token, useProjectHostname],\n )\n const [longPollingInterval, setLongPollingInterval] = useState<number | false>(false)\n const [resolvedInitialPerspective, setResolvedInitialPerspective] = useState(false)\n\n /**\n * 1. Handle Live Events and call revalidateTag or router.refresh when needed\n */\n const router = useRouter()\n const handleLiveEvent = useEffectEvent((event: LiveEvent) => {\n if (process.env.NODE_ENV !== 'production' && event.type === 'welcome') {\n // oxlint-disable-next-line no-console\n console.info(\n 'Sanity is live with',\n token\n ? 'automatic revalidation for draft content changes as well as published content'\n : draftModeEnabled\n ? 'automatic revalidation for only published content. Provide a `browserToken` to `defineLive` to support draft content outside of Presentation Tool.'\n : 'automatic revalidation of published content',\n )\n // Disable long polling when welcome event is received, this is a no-op if long polling is already disabled\n setLongPollingInterval(false)\n } else if (event.type === 'message') {\n void revalidateSyncTags(\n event.tags.map((tag: SyncTag) => `${PUBLISHED_SYNC_TAG_PREFIX}${tag}` as const),\n ).then((result) => {\n if (result === 'refresh') router.refresh()\n })\n } else if (event.type === 'restart' || event.type === 'reconnect') {\n router.refresh()\n } else if (event.type === 'goaway') {\n onGoAway(event, intervalOnGoAway)\n setLongPollingInterval(intervalOnGoAway)\n }\n })\n // @TODO previous version that handle both published and draft events\n // useEffect(() => {\n // const subscription = client.live.events({includeDrafts: !!token, tag: requestTag}).subscribe({\n // next: handleLiveEvent,\n // error: (err: unknown) => {\n // onError(err)\n // },\n // })\n // return () => subscription.unsubscribe()\n // }, [client.live, onError, requestTag, token])\n useEffect(() => {\n const subscription = client.live.events({tag: requestTag}).subscribe({\n next: handleLiveEvent,\n error: (err: unknown) => {\n onError(err)\n },\n })\n return () => subscription.unsubscribe()\n }, [client.live, onError, requestTag, token])\n\n /**\n * Handle live events for drafts differently, only use it to trigger refreshes, don't expire the cache\n */\n const handleLiveDraftEvent = useEffectEvent((event: LiveEvent) => {\n if (event.type === 'message') {\n // Just refresh, due to cache bypass in draft mode it'll fetch fresh content (though we wish cache worked as in production)\n // @TODO if draft content is published, then this extra refresh is unnecessary, it's tricky to check since `event.id` are different on the two EventSource connections\n router.refresh()\n }\n })\n useEffect(() => {\n if (!token) return\n const subscription = client.live.events({includeDrafts: !!token, tag: requestTag}).subscribe({\n next: handleLiveDraftEvent,\n error: (err: unknown) => {\n onError(err)\n },\n })\n return () => subscription.unsubscribe()\n }, [client.live, onError, requestTag, token])\n\n /**\n * 2. Notify what perspective we're in, when in Draft Mode\n */\n useEffect(() => {\n if (resolvedInitialPerspective) return undefined\n\n if (!draftModeEnabled) {\n setResolvedInitialPerspective(true)\n setPerspective('unknown')\n return undefined\n }\n\n const controller = new AbortController()\n resolveDraftModePerspective()\n .then((perspective) => {\n if (controller.signal.aborted) return\n setResolvedInitialPerspective(true)\n setPerspective(sanitizePerspective(perspective, 'drafts'))\n })\n .catch((err) => {\n if (controller.signal.aborted) return\n console.error('Failed to resolve draft mode perspective', err)\n setResolvedInitialPerspective(true)\n setPerspective('unknown')\n })\n return () => controller.abort()\n }, [draftModeEnabled, resolveDraftModePerspective, resolvedInitialPerspective])\n\n const [loadComlink, setLoadComlink] = useState(false)\n /**\n * 3. Notify what environment we're in, when in Draft Mode\n */\n useEffect(() => {\n // If we might be in Presentation Tool, then skip detecting here as it's handled later\n if (isMaybePresentation()) return\n\n // If we're definitely not in Presentation Tool, then we can set the environment as stand-alone live preview\n // if we have both a browser token, and draft mode is enabled\n if (draftModeEnabled && token) {\n setEnvironment('live')\n return\n }\n // If we're in draft mode, but don't have a browser token, then we're in static mode\n // which means that published content is still live, but draft changes likely need manual refresh\n if (draftModeEnabled) {\n setEnvironment('static')\n return\n }\n\n // Fallback to `unknown` otherwise, as we simply don't know how it's setup\n setEnvironment('unknown')\n return\n }, [draftModeEnabled, token])\n\n /**\n * 4. If Presentation Tool is detected, load up the comlink and integrate with it\n */\n useEffect(() => {\n if (!isMaybePresentation()) return\n const controller = new AbortController()\n // Wait for a while to see if Presentation Tool is detected, before assuming the env to be stand-alone live preview\n const timeout = setTimeout(() => setEnvironment('live'), 3_000)\n window.addEventListener(\n 'message',\n ({data}: MessageEvent<unknown>) => {\n if (\n data &&\n typeof data === 'object' &&\n 'domain' in data &&\n data.domain === 'sanity/channels' &&\n 'from' in data &&\n data.from === 'presentation'\n ) {\n clearTimeout(timeout)\n setEnvironment(isMaybePreviewWindow() ? 'presentation-window' : 'presentation-iframe')\n setLoadComlink(true)\n controller.abort()\n }\n },\n {signal: controller.signal},\n )\n return () => {\n clearTimeout(timeout)\n controller.abort()\n }\n }, [])\n\n /**\n * 5. Warn if draft mode is being disabled\n * @TODO move logic into PresentationComlink, or maybe VisualEditing?\n */\n const draftModeEnabledWarnRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n useEffect(() => {\n if (!draftModeEnabled) return\n clearTimeout(draftModeEnabledWarnRef.current)\n return () => {\n draftModeEnabledWarnRef.current = setTimeout(() => {\n console.warn('Sanity Live: Draft mode was enabled, but is now being disabled')\n })\n }\n }, [draftModeEnabled])\n\n /**\n * 6. Handle switching to long polling when needed\n */\n useEffect(() => {\n if (!longPollingInterval) return\n const interval = setInterval(() => router.refresh(), longPollingInterval)\n return () => clearInterval(interval)\n }, [longPollingInterval, router])\n\n return (\n <>\n {draftModeEnabled && loadComlink && resolvedInitialPerspective && (\n <PresentationComlink\n projectId={projectId!}\n dataset={dataset!}\n draftModeEnabled={draftModeEnabled}\n />\n )}\n {!draftModeEnabled && refreshOnMount && <RefreshOnMount />}\n {!draftModeEnabled && refreshOnFocus && <RefreshOnFocus />}\n {!draftModeEnabled && refreshOnReconnect && <RefreshOnReconnect />}\n </>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.d.ts","names":[],"sources":["../../src/experimental/live.tsx"],"sourcesContent":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"live.d.ts","names":[],"sources":["../../src/experimental/live.tsx"],"sourcesContent":[],"mappings":";;;;;AA6BA;;AAGqC,iBAHf,6BAAA,CAGe;EAAA,OAAA,EAF1B;CAE0B,EAAA;EAAlB,OAAA,EAAR,OAAQ,CAAA,UAAA,CAAA,OAAkB,OAAlB,CAAA,CAAA;CAAR,CAAA,EACP,OADO,CACC,OADD,CACS,iBADT,EAAA,KAAA,CAAA,CAAA;;;;AACP,UAoEa,kBApEb,CAAA,oBAAA,MAAA,CAAA,CAAA;EAoEJ,KAAiB,EACR,WADQ;EACR,MAAA,CAAA,EACE,WADF;EACE;;;EAIK,WAAA,CAAA,EAAA,OAAA,CAAQ,iBAAR,EAAA,KAAA,CAAA;EAqBhB;;;;EAGQ,KAAA,CAAA,EAAA,OAAA;EAKK;;;AAUb;;EAoDoB,UAAA,CAAA,EAAA,MAAA;EAAmC;;;EAUT,IAAA,CAAA,EAAA,MAAA,EAAA;;;AAM9C;AAoBA;AAAmC,KA1GvB,sBAAA,GA0GuB,CAAA,0BAAA,MAAA,CAAA,CAAA,OAAA,EAzGxB,kBAyGwB,CAzGL,WAyGK,CAAA,EAAA,GAxG9B,OAwG8B,CAAA;EAIpB,IAAA,EA3GP,YA2GO,CA3GM,WA2GN,EAAA,OAAA,CAAA;EAImB;;;;aA1GrB;;;;;;;;;UAUI,sBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA4CI;;;;;wCAQD,mCAAmC,wBAAwB,gBACxE;;;;sCAS+B,QAAQ;;;;;UAM7B,uBAAA;;;;UAIP;;;;;;;;;;;;;;;iBAgBM,UAAA,SAAmB;;;;eAIpB;;;;cAID,KAAA,CAAM,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.js","names":["SanityLiveServerComponent: React.ComponentType<SanityLiveServerComponentProps>","SanityLiveServerComponent","resolveDraftModePerspective"],"sources":["../../src/experimental/live.tsx"],"sourcesContent":["// oxlint-disable-next-line no-unassigned-import\nimport 'server-only'\nimport {\n createClient,\n type ClientPerspective,\n type ClientReturn,\n type ContentSourceMap,\n type LiveEventGoAway,\n type QueryParams,\n type SanityClient,\n type SyncTag,\n} from 'next-sanity'\nimport {stegaEncodeSourceMap} from '@sanity/client/stega'\nimport SanityLiveClientComponent, {\n type SanityLiveProps,\n} from 'next-sanity/experimental/client-components/live'\nimport {cacheTag, cacheLife, updateTag} from 'next/cache'\nimport {draftMode, cookies} from 'next/headers'\nimport {preconnect} from 'react-dom'\nimport {perspectiveCookieName} from '@sanity/preview-url-secret/constants'\nimport {sanitizePerspective} from '../live/utils'\nimport type {SanityClientConfig} from './types'\nimport {DRAFT_SYNC_TAG_PREFIX, PUBLISHED_SYNC_TAG_PREFIX} from './constants'\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport async function resolvePerspectiveFromCookies({\n cookies: jar,\n}: {\n cookies: Awaited<ReturnType<typeof cookies>>\n}): Promise<Exclude<ClientPerspective, 'raw'>> {\n return jar.has(perspectiveCookieName)\n ? sanitizePerspective(jar.get(perspectiveCookieName)?.value, 'drafts')\n : 'drafts'\n}\n\nasync function sanityCachedFetch<const QueryString extends string>(\n config: SanityClientConfig,\n {\n query,\n params = {},\n perspective,\n stega,\n requestTag,\n draftToken,\n customCacheTags = [],\n }: {\n query: QueryString\n params?: QueryParams\n perspective: Exclude<ClientPerspective, 'raw'>\n stega: boolean\n requestTag: string\n draftToken?: string | false | undefined\n customCacheTags?: string[]\n },\n): Promise<{\n data: ClientReturn<QueryString, unknown>\n sourceMap: ContentSourceMap | null\n tags: string[]\n}> {\n 'use cache: remote'\n\n const client = createClient({...config, useCdn: true})\n const useCdn = perspective === 'published'\n\n const {result, resultSourceMap, syncTags} = await client.fetch(query, params, {\n filterResponse: false,\n returnQuery: false,\n perspective,\n useCdn,\n resultSourceMap: stega ? 'withKeyArraySelector' : undefined, // @TODO allow passing csm for non-stega use\n stega: false,\n cacheMode: useCdn ? 'noStale' : undefined,\n tag: requestTag,\n token: perspective === 'published' ? config.token : draftToken || config.token, // @TODO can pass undefined instead of config.token here?\n })\n const tags = [\n ...customCacheTags,\n ...(syncTags || []).map(\n (tag) =>\n `${perspective === 'published' ? PUBLISHED_SYNC_TAG_PREFIX : DRAFT_SYNC_TAG_PREFIX}${tag}`,\n ),\n ]\n /**\n * The tags used here, are expired later on in the `expireTags` Server Action with the `expireTag` function from `next/cache`\n */\n cacheTag(...tags)\n /**\n * Sanity Live handles on-demand revalidation, so the default 15min time based revalidation is too short\n */\n cacheLife({revalidate: 60 * 60 * 24 * 90})\n\n return {data: result, sourceMap: resultSourceMap || null, tags}\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport interface SanityFetchOptions<QueryString extends string> {\n query: QueryString\n params?: QueryParams\n /**\n * @defaultValue 'published'\n */\n perspective?: Exclude<ClientPerspective, 'raw'>\n /**\n * Enables stega encoding of the data, this is typically only used in draft mode in conjunction with `perspective: 'drafts'` and with `@sanity/visual-editing` setup.\n * @defaultValue `false`\n */\n stega?: boolean\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.fetch'\n */\n requestTag?: string\n /**\n * Custom cache tags that can be used with next's `revalidateTag` and `updateTag` functions for custom webhook on-demand revalidation.\n */\n tags?: string[]\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport type DefinedSanityFetchType = <const QueryString extends string>(\n options: SanityFetchOptions<QueryString>,\n) => Promise<{\n data: ClientReturn<QueryString, unknown>\n /**\n * The Content Source Map can be used for custom setups like `encodeSourceMap` for `data-sanity` attributes, or `stegaEncodeSourceMap` for stega encoding in your own way.\n * The Content Source Map is only fetched by default in draft mode, if `stega` is `true`. Otherwise your client configuration will need to have `resultSourceMap: 'withKeyArraySelector' | true`\n */\n sourceMap: ContentSourceMap | null\n /**\n * The cache tags used with `next/cache`, useful for debugging.\n */\n tags: string[]\n}>\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport interface DefinedSanityLiveProps {\n /**\n * Automatic refresh of RSC when the component <SanityLive /> is mounted.\n * @defaultValue `false`\n */\n refreshOnMount?: boolean\n /**\n * Automatically refresh when window gets focused\n * @defaultValue `false`\n */\n refreshOnFocus?: boolean\n /**\n * Automatically refresh when the browser regains a network connection (via navigator.onLine)\n * @defaultValue `false`\n */\n refreshOnReconnect?: boolean\n /**\n * Automatically refresh on an interval when the Live Event API emits a `goaway` event, which indicates that the connection is rejected or closed.\n * This typically happens if the connection limit is reached, or if the connection is idle for too long.\n * To disable this long polling fallback behavior set `intervalOnGoAway` to `false` or `0`.\n * You can also use `onGoAway` to handle the `goaway` event in your own way, and read the reason why the event was emitted.\n * @defaultValue `30_000` 30 seconds interval\n */\n intervalOnGoAway?: number | false\n\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.live'\n */\n requestTag?: string\n\n /**\n * Handle errors from the Live Events subscription.\n * By default it's reported using `console.error`, you can override this prop to handle it in your own way.\n */\n onError?: (error: unknown) => void\n\n /**\n * Handle the `goaway` event if the connection is rejected/closed.\n * `event.reason` will be a string of why the event was emitted, for example `'connection limit reached'`.\n * When this happens the `<SanityLive />` will fallback to long polling with a default interval of 30 seconds, providing your own `onGoAway` handler does not change this behavior.\n * If you want to disable long polling set `intervalOnGoAway` to `false` or `0`.\n */\n onGoAway?: (event: LiveEventGoAway, intervalOnGoAway: number | false) => void\n\n /**\n * Override how cache tags are invalidated, you need to pass a server action here.\n * You can also pass a `use client` function here, and have `router.refresh()` be called if the promise resolves to `'refresh'`.\n */\n // @TODO remove, replace with onLiveEvent\n revalidateSyncTags?: (\n tags: `${typeof PUBLISHED_SYNC_TAG_PREFIX | typeof DRAFT_SYNC_TAG_PREFIX}${SyncTag}`[],\n ) => Promise<void | 'refresh'>\n\n // @TODO add\n // decide how to handle a live event coming in\n // onLiveEvent?: (event: LiveEvent, mode: 'production' | 'preview) => void\n\n /**\n * Control how the draft mode perspective is resolved, by default it resolves from the `sanity-preview-perspective` cookie.\n */\n resolveDraftModePerspective?: () => Promise<ClientPerspective>\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport interface DefineSanityLiveOptions {\n /**\n * Required for `sanityFetch` and `SanityLive` to work\n */\n client: SanityClient\n /**\n * Optional. If provided then the token needs to have permissions to query documents with `drafts.` prefixes in order for `perspective: 'drafts'` to work.\n * This token is not shared with the browser.\n */\n serverToken?: string | false\n /**\n * Optional. This token is shared with the browser, and should only have access to query published documents.\n * It is used to setup a `Live Draft Content` EventSource connection, and enables live previewing drafts stand-alone, outside of Presentation Tool.\n */\n browserToken?: string | false\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport function defineLive(config: DefineSanityLiveOptions): {\n /**\n * Use this function to fetch data from Sanity in your React Server Components.\n */\n sanityFetch: DefinedSanityFetchType\n /**\n * Render this in your root layout.tsx to make your page revalidate on new content live, automatically.\n */\n SanityLive: React.ComponentType<DefinedSanityLiveProps>\n} {\n const {client: _client, serverToken, browserToken} = config\n\n if (!_client) {\n throw new Error('`client` is required for `defineLive` to function')\n }\n\n if (process.env.NODE_ENV !== 'production' && !serverToken && serverToken !== false) {\n // eslint-disable-next-line no-console\n console.warn(\n 'No `serverToken` provided to `defineLive`. This means that only published content will be fetched and respond to live events. You can silence this warning by setting `serverToken: false`.',\n )\n }\n\n if (process.env.NODE_ENV !== 'production' && !browserToken && browserToken !== false) {\n // eslint-disable-next-line no-console\n console.warn(\n 'No `browserToken` provided to `defineLive`. This means that live previewing drafts will only work when using the Presentation Tool in your Sanity Studio. To support live previewing drafts stand-alone, provide a `browserToken`. It is shared with the browser so it should only have Viewer rights or lower. You can silence this warning by setting `browserToken: false`.',\n )\n }\n\n const client = _client.withConfig({allowReconfigure: false, useCdn: false})\n const {\n token: originalToken,\n apiHost,\n apiVersion,\n useProjectHostname,\n dataset,\n projectId,\n requestTagPrefix,\n stega: stegaConfig,\n } = client.config()\n\n const sanityFetch: DefinedSanityFetchType = function sanityFetch<\n const QueryString extends string,\n >({\n query,\n params = {},\n stega = false,\n perspective = 'published',\n tags: customCacheTags = [],\n requestTag = 'next-loader.fetch',\n }: {\n query: QueryString\n params?: QueryParams\n stega?: boolean\n tags?: string[]\n perspective?: Exclude<ClientPerspective, 'raw'>\n requestTag?: string\n }) {\n return sanityCachedFetch(\n {\n apiHost,\n apiVersion,\n useProjectHostname,\n dataset,\n projectId,\n requestTagPrefix,\n token: originalToken,\n },\n {\n query,\n params,\n perspective,\n stega,\n requestTag,\n draftToken: serverToken,\n customCacheTags,\n },\n ).then(({data, sourceMap, tags}) => ({\n data:\n stega && sourceMap\n ? stegaEncodeSourceMap(data, sourceMap, {...stegaConfig, enabled: true})\n : data,\n sourceMap,\n tags,\n }))\n }\n\n const SanityLive: React.ComponentType<DefinedSanityLiveProps> = function SanityLive(props) {\n const {\n // perspective,\n refreshOnMount = false,\n refreshOnFocus = false,\n refreshOnReconnect = false,\n requestTag,\n onError,\n onGoAway,\n intervalOnGoAway,\n revalidateSyncTags = expireTags,\n } = props\n\n const {projectId, dataset, apiHost, apiVersion, useProjectHostname, requestTagPrefix} =\n client.config()\n const {origin} = new URL(client.getUrl('', false))\n\n // Preconnect to the Live Event API origin early, as the Sanity API is almost always on a different origin than the app\n preconnect(origin)\n\n return (\n <SanityLiveServerComponent\n config={{projectId, dataset, apiHost, apiVersion, useProjectHostname, requestTagPrefix}}\n requestTag={requestTag}\n browserToken={browserToken}\n // origin={origin}\n refreshOnMount={refreshOnMount}\n refreshOnFocus={refreshOnFocus}\n refreshOnReconnect={refreshOnReconnect}\n onError={onError}\n onGoAway={onGoAway}\n intervalOnGoAway={intervalOnGoAway}\n revalidateSyncTags={revalidateSyncTags}\n resolveDraftModePerspective={\n props.resolveDraftModePerspective ?? resolveDraftModePerspective\n }\n />\n )\n }\n\n return {sanityFetch, SanityLive}\n}\n\ninterface SanityLiveServerComponentProps extends Omit<\n SanityLiveProps,\n 'draftModeEnabled' | 'token' | 'draftModePerspective'\n> {\n browserToken: string | false | undefined\n // origin: string\n // perspective?: Exclude<ClientPerspective, 'raw'>\n}\n\nconst SanityLiveServerComponent: React.ComponentType<SanityLiveServerComponentProps> =\n async function SanityLiveServerComponent(props) {\n // 'use cache'\n // @TODO should this be 'max' instead?, or configured by changing the default cache profile?\n // cacheLife({\n // stale: Infinity,\n // revalidate: Infinity,\n // expire: Infinity,\n // })\n const {\n config,\n requestTag,\n intervalOnGoAway,\n onError,\n onGoAway,\n refreshOnFocus,\n refreshOnMount,\n refreshOnReconnect,\n revalidateSyncTags,\n browserToken,\n // origin,\n // perspective,\n resolveDraftModePerspective,\n } = props\n\n const {isEnabled: isDraftModeEnabled} = await draftMode()\n\n // // Preconnect to the Live Event API origin early, as the Sanity API is almost always on a different origin than the app\n // preconnect(origin)\n\n return (\n <SanityLiveClientComponent\n config={{\n ...config,\n token: typeof browserToken === 'string' && isDraftModeEnabled ? browserToken : undefined,\n }}\n requestTag={requestTag}\n draftModeEnabled={isDraftModeEnabled}\n refreshOnMount={refreshOnMount}\n refreshOnFocus={refreshOnFocus}\n refreshOnReconnect={refreshOnReconnect}\n onError={onError}\n onGoAway={onGoAway}\n intervalOnGoAway={intervalOnGoAway}\n revalidateSyncTags={revalidateSyncTags}\n resolveDraftModePerspective={resolveDraftModePerspective}\n />\n )\n }\n\n// @TODO expose parseTags function that returns the correct array of tags\n// we already have s1: prefixes, but they could change\n// use sp: for prod, sd: for draft, keep em short\nasync function expireTags(_tags: unknown): Promise<void> {\n 'use server'\n // @TODO Draft Mode bypasses cache anyway so we don't bother with expiring tags for draft content\n // const isDraftMode = (await draftMode()).isEnabled\n // const tags = _tags.map((tag) => `${isDraftMode ? 'drafts' : 'sanity'}:${tag}`)\n if (!Array.isArray(_tags)) {\n console.warn('<SanityLive /> `expireTags` called with non-array tags', _tags)\n return undefined\n }\n const tags = _tags.filter(\n (tag) => typeof tag === 'string' && tag.startsWith(PUBLISHED_SYNC_TAG_PREFIX),\n )\n if (!tags.length) {\n console.warn('<SanityLive /> `expireTags` called with no valid tags', _tags)\n return undefined\n }\n for (const tag of tags) {\n updateTag(tag)\n }\n console.log(`<SanityLive /> updated tags: ${tags.join(', ')}`)\n}\n\nasync function resolveDraftModePerspective(): Promise<ClientPerspective> {\n 'use server'\n if ((await draftMode()).isEnabled) {\n const jar = await cookies()\n return resolvePerspectiveFromCookies({cookies: jar})\n }\n return 'published'\n}\n\n// revalidateSyncTags => actionUpdateTags\n// router.refresh() => actionRefresh\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"live.js","names":["SanityLiveServerComponent: React.ComponentType<SanityLiveServerComponentProps>","SanityLiveServerComponent","resolveDraftModePerspective"],"sources":["../../src/experimental/live.tsx"],"sourcesContent":["// oxlint-disable-next-line no-unassigned-import\nimport 'server-only'\nimport {stegaEncodeSourceMap} from '@sanity/client/stega'\nimport {perspectiveCookieName} from '@sanity/preview-url-secret/constants'\nimport {\n createClient,\n type ClientPerspective,\n type ClientReturn,\n type ContentSourceMap,\n type LiveEventGoAway,\n type QueryParams,\n type SanityClient,\n type SyncTag,\n} from 'next-sanity'\nimport SanityLiveClientComponent, {\n type SanityLiveProps,\n} from 'next-sanity/experimental/client-components/live'\nimport {cacheTag, cacheLife, updateTag} from 'next/cache'\nimport {draftMode, cookies} from 'next/headers'\nimport {preconnect} from 'react-dom'\n\nimport type {SanityClientConfig} from './types'\n\nimport {sanitizePerspective} from '../live/utils'\nimport {DRAFT_SYNC_TAG_PREFIX, PUBLISHED_SYNC_TAG_PREFIX} from './constants'\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport async function resolvePerspectiveFromCookies({\n cookies: jar,\n}: {\n cookies: Awaited<ReturnType<typeof cookies>>\n}): Promise<Exclude<ClientPerspective, 'raw'>> {\n return jar.has(perspectiveCookieName)\n ? sanitizePerspective(jar.get(perspectiveCookieName)?.value, 'drafts')\n : 'drafts'\n}\n\nasync function sanityCachedFetch<const QueryString extends string>(\n config: SanityClientConfig,\n {\n query,\n params = {},\n perspective,\n stega,\n requestTag,\n draftToken,\n customCacheTags = [],\n }: {\n query: QueryString\n params?: QueryParams\n perspective: Exclude<ClientPerspective, 'raw'>\n stega: boolean\n requestTag: string\n draftToken?: string | false | undefined\n customCacheTags?: string[]\n },\n): Promise<{\n data: ClientReturn<QueryString, unknown>\n sourceMap: ContentSourceMap | null\n tags: string[]\n}> {\n 'use cache: remote'\n\n const client = createClient({...config, useCdn: true})\n const useCdn = perspective === 'published'\n\n const {result, resultSourceMap, syncTags} = await client.fetch(query, params, {\n filterResponse: false,\n returnQuery: false,\n perspective,\n useCdn,\n resultSourceMap: stega ? 'withKeyArraySelector' : undefined, // @TODO allow passing csm for non-stega use\n stega: false,\n cacheMode: useCdn ? 'noStale' : undefined,\n tag: requestTag,\n token: perspective === 'published' ? config.token : draftToken || config.token, // @TODO can pass undefined instead of config.token here?\n })\n const tags = [\n ...customCacheTags,\n ...(syncTags || []).map(\n (tag) =>\n `${perspective === 'published' ? PUBLISHED_SYNC_TAG_PREFIX : DRAFT_SYNC_TAG_PREFIX}${tag}`,\n ),\n ]\n /**\n * The tags used here, are expired later on in the `expireTags` Server Action with the `expireTag` function from `next/cache`\n */\n cacheTag(...tags)\n /**\n * Sanity Live handles on-demand revalidation, so the default 15min time based revalidation is too short\n */\n cacheLife({revalidate: 60 * 60 * 24 * 90})\n\n return {data: result, sourceMap: resultSourceMap || null, tags}\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport interface SanityFetchOptions<QueryString extends string> {\n query: QueryString\n params?: QueryParams\n /**\n * @defaultValue 'published'\n */\n perspective?: Exclude<ClientPerspective, 'raw'>\n /**\n * Enables stega encoding of the data, this is typically only used in draft mode in conjunction with `perspective: 'drafts'` and with `@sanity/visual-editing` setup.\n * @defaultValue `false`\n */\n stega?: boolean\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.fetch'\n */\n requestTag?: string\n /**\n * Custom cache tags that can be used with next's `revalidateTag` and `updateTag` functions for custom webhook on-demand revalidation.\n */\n tags?: string[]\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport type DefinedSanityFetchType = <const QueryString extends string>(\n options: SanityFetchOptions<QueryString>,\n) => Promise<{\n data: ClientReturn<QueryString, unknown>\n /**\n * The Content Source Map can be used for custom setups like `encodeSourceMap` for `data-sanity` attributes, or `stegaEncodeSourceMap` for stega encoding in your own way.\n * The Content Source Map is only fetched by default in draft mode, if `stega` is `true`. Otherwise your client configuration will need to have `resultSourceMap: 'withKeyArraySelector' | true`\n */\n sourceMap: ContentSourceMap | null\n /**\n * The cache tags used with `next/cache`, useful for debugging.\n */\n tags: string[]\n}>\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport interface DefinedSanityLiveProps {\n /**\n * Automatic refresh of RSC when the component <SanityLive /> is mounted.\n * @defaultValue `false`\n */\n refreshOnMount?: boolean\n /**\n * Automatically refresh when window gets focused\n * @defaultValue `false`\n */\n refreshOnFocus?: boolean\n /**\n * Automatically refresh when the browser regains a network connection (via navigator.onLine)\n * @defaultValue `false`\n */\n refreshOnReconnect?: boolean\n /**\n * Automatically refresh on an interval when the Live Event API emits a `goaway` event, which indicates that the connection is rejected or closed.\n * This typically happens if the connection limit is reached, or if the connection is idle for too long.\n * To disable this long polling fallback behavior set `intervalOnGoAway` to `false` or `0`.\n * You can also use `onGoAway` to handle the `goaway` event in your own way, and read the reason why the event was emitted.\n * @defaultValue `30_000` 30 seconds interval\n */\n intervalOnGoAway?: number | false\n\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.live'\n */\n requestTag?: string\n\n /**\n * Handle errors from the Live Events subscription.\n * By default it's reported using `console.error`, you can override this prop to handle it in your own way.\n */\n onError?: (error: unknown) => void\n\n /**\n * Handle the `goaway` event if the connection is rejected/closed.\n * `event.reason` will be a string of why the event was emitted, for example `'connection limit reached'`.\n * When this happens the `<SanityLive />` will fallback to long polling with a default interval of 30 seconds, providing your own `onGoAway` handler does not change this behavior.\n * If you want to disable long polling set `intervalOnGoAway` to `false` or `0`.\n */\n onGoAway?: (event: LiveEventGoAway, intervalOnGoAway: number | false) => void\n\n /**\n * Override how cache tags are invalidated, you need to pass a server action here.\n * You can also pass a `use client` function here, and have `router.refresh()` be called if the promise resolves to `'refresh'`.\n */\n // @TODO remove, replace with onLiveEvent\n revalidateSyncTags?: (\n tags: `${typeof PUBLISHED_SYNC_TAG_PREFIX | typeof DRAFT_SYNC_TAG_PREFIX}${SyncTag}`[],\n ) => Promise<void | 'refresh'>\n\n // @TODO add\n // decide how to handle a live event coming in\n // onLiveEvent?: (event: LiveEvent, mode: 'production' | 'preview) => void\n\n /**\n * Control how the draft mode perspective is resolved, by default it resolves from the `sanity-preview-perspective` cookie.\n */\n resolveDraftModePerspective?: () => Promise<ClientPerspective>\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport interface DefineSanityLiveOptions {\n /**\n * Required for `sanityFetch` and `SanityLive` to work\n */\n client: SanityClient\n /**\n * Optional. If provided then the token needs to have permissions to query documents with `drafts.` prefixes in order for `perspective: 'drafts'` to work.\n * This token is not shared with the browser.\n */\n serverToken?: string | false\n /**\n * Optional. This token is shared with the browser, and should only have access to query published documents.\n * It is used to setup a `Live Draft Content` EventSource connection, and enables live previewing drafts stand-alone, outside of Presentation Tool.\n */\n browserToken?: string | false\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport function defineLive(config: DefineSanityLiveOptions): {\n /**\n * Use this function to fetch data from Sanity in your React Server Components.\n */\n sanityFetch: DefinedSanityFetchType\n /**\n * Render this in your root layout.tsx to make your page revalidate on new content live, automatically.\n */\n SanityLive: React.ComponentType<DefinedSanityLiveProps>\n} {\n const {client: _client, serverToken, browserToken} = config\n\n if (!_client) {\n throw new Error('`client` is required for `defineLive` to function')\n }\n\n if (process.env.NODE_ENV !== 'production' && !serverToken && serverToken !== false) {\n console.warn(\n 'No `serverToken` provided to `defineLive`. This means that only published content will be fetched and respond to live events. You can silence this warning by setting `serverToken: false`.',\n )\n }\n\n if (process.env.NODE_ENV !== 'production' && !browserToken && browserToken !== false) {\n console.warn(\n 'No `browserToken` provided to `defineLive`. This means that live previewing drafts will only work when using the Presentation Tool in your Sanity Studio. To support live previewing drafts stand-alone, provide a `browserToken`. It is shared with the browser so it should only have Viewer rights or lower. You can silence this warning by setting `browserToken: false`.',\n )\n }\n\n const client = _client.withConfig({allowReconfigure: false, useCdn: false})\n const {\n token: originalToken,\n apiHost,\n apiVersion,\n useProjectHostname,\n dataset,\n projectId,\n requestTagPrefix,\n stega: stegaConfig,\n } = client.config()\n\n const sanityFetch: DefinedSanityFetchType = function sanityFetch<\n const QueryString extends string,\n >({\n query,\n params = {},\n stega = false,\n perspective = 'published',\n tags: customCacheTags = [],\n requestTag = 'next-loader.fetch',\n }: {\n query: QueryString\n params?: QueryParams\n stega?: boolean\n tags?: string[]\n perspective?: Exclude<ClientPerspective, 'raw'>\n requestTag?: string\n }) {\n return sanityCachedFetch(\n {\n apiHost,\n apiVersion,\n useProjectHostname,\n dataset,\n projectId,\n requestTagPrefix,\n token: originalToken,\n },\n {\n query,\n params,\n perspective,\n stega,\n requestTag,\n draftToken: serverToken,\n customCacheTags,\n },\n ).then(({data, sourceMap, tags}) => ({\n data:\n stega && sourceMap\n ? stegaEncodeSourceMap(data, sourceMap, {...stegaConfig, enabled: true})\n : data,\n sourceMap,\n tags,\n }))\n }\n\n const SanityLive: React.ComponentType<DefinedSanityLiveProps> = function SanityLive(props) {\n const {\n // perspective,\n refreshOnMount = false,\n refreshOnFocus = false,\n refreshOnReconnect = false,\n requestTag,\n onError,\n onGoAway,\n intervalOnGoAway,\n revalidateSyncTags = expireTags,\n } = props\n\n const {projectId, dataset, apiHost, apiVersion, useProjectHostname, requestTagPrefix} =\n client.config()\n const {origin} = new URL(client.getUrl('', false))\n\n // Preconnect to the Live Event API origin early, as the Sanity API is almost always on a different origin than the app\n preconnect(origin)\n\n return (\n <SanityLiveServerComponent\n config={{projectId, dataset, apiHost, apiVersion, useProjectHostname, requestTagPrefix}}\n requestTag={requestTag}\n browserToken={browserToken}\n // origin={origin}\n refreshOnMount={refreshOnMount}\n refreshOnFocus={refreshOnFocus}\n refreshOnReconnect={refreshOnReconnect}\n onError={onError}\n onGoAway={onGoAway}\n intervalOnGoAway={intervalOnGoAway}\n revalidateSyncTags={revalidateSyncTags}\n resolveDraftModePerspective={\n props.resolveDraftModePerspective ?? resolveDraftModePerspective\n }\n />\n )\n }\n\n return {sanityFetch, SanityLive}\n}\n\ninterface SanityLiveServerComponentProps extends Omit<\n SanityLiveProps,\n 'draftModeEnabled' | 'token' | 'draftModePerspective'\n> {\n browserToken: string | false | undefined\n // origin: string\n // perspective?: Exclude<ClientPerspective, 'raw'>\n}\n\nconst SanityLiveServerComponent: React.ComponentType<SanityLiveServerComponentProps> =\n async function SanityLiveServerComponent(props) {\n // 'use cache'\n // @TODO should this be 'max' instead?, or configured by changing the default cache profile?\n // cacheLife({\n // stale: Infinity,\n // revalidate: Infinity,\n // expire: Infinity,\n // })\n const {\n config,\n requestTag,\n intervalOnGoAway,\n onError,\n onGoAway,\n refreshOnFocus,\n refreshOnMount,\n refreshOnReconnect,\n revalidateSyncTags,\n browserToken,\n // origin,\n // perspective,\n resolveDraftModePerspective,\n } = props\n\n const {isEnabled: isDraftModeEnabled} = await draftMode()\n\n // // Preconnect to the Live Event API origin early, as the Sanity API is almost always on a different origin than the app\n // preconnect(origin)\n\n return (\n <SanityLiveClientComponent\n config={{\n ...config,\n token: typeof browserToken === 'string' && isDraftModeEnabled ? browserToken : undefined,\n }}\n requestTag={requestTag}\n draftModeEnabled={isDraftModeEnabled}\n refreshOnMount={refreshOnMount}\n refreshOnFocus={refreshOnFocus}\n refreshOnReconnect={refreshOnReconnect}\n onError={onError}\n onGoAway={onGoAway}\n intervalOnGoAway={intervalOnGoAway}\n revalidateSyncTags={revalidateSyncTags}\n resolveDraftModePerspective={resolveDraftModePerspective}\n />\n )\n }\n\n// @TODO expose parseTags function that returns the correct array of tags\n// we already have s1: prefixes, but they could change\n// use sp: for prod, sd: for draft, keep em short\nasync function expireTags(_tags: unknown): Promise<void> {\n 'use server'\n // @TODO Draft Mode bypasses cache anyway so we don't bother with expiring tags for draft content\n // const isDraftMode = (await draftMode()).isEnabled\n // const tags = _tags.map((tag) => `${isDraftMode ? 'drafts' : 'sanity'}:${tag}`)\n if (!Array.isArray(_tags)) {\n console.warn('<SanityLive /> `expireTags` called with non-array tags', _tags)\n return undefined\n }\n const tags = _tags.filter(\n (tag) => typeof tag === 'string' && tag.startsWith(PUBLISHED_SYNC_TAG_PREFIX),\n )\n if (!tags.length) {\n console.warn('<SanityLive /> `expireTags` called with no valid tags', _tags)\n return undefined\n }\n for (const tag of tags) {\n updateTag(tag)\n }\n // oxlint-disable-next-line no-console\n console.log(`<SanityLive /> updated tags: ${tags.join(', ')}`)\n}\n\nasync function resolveDraftModePerspective(): Promise<ClientPerspective> {\n 'use server'\n if ((await draftMode()).isEnabled) {\n const jar = await cookies()\n return resolvePerspectiveFromCookies({cookies: jar})\n }\n return 'published'\n}\n\n// revalidateSyncTags => actionUpdateTags\n// router.refresh() => actionRefresh\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":["perspectiveListeners: Set<() => void>","perspective: DraftPerspective","environmentListeners: Set<() => void>","environment: DraftEnvironment","comlinkListeners: Set<() => void>","comlink: Node<LoaderNodeMsg, LoaderControllerMsg> | null","comlinkProjectId: string | null","comlinkDataset: string | null"],"sources":["../../src/live/hooks/context.ts","../../src/live/hooks/useDraftMode.ts","../../src/live/hooks/useIsPresentationTool.ts","../../src/live/hooks/useIsLivePreview.ts","../../src/live/hooks/usePresentationQuery.ts"],"sourcesContent":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":["perspectiveListeners: Set<() => void>","perspective: DraftPerspective","environmentListeners: Set<() => void>","environment: DraftEnvironment","comlinkListeners: Set<() => void>","comlink: Node<LoaderNodeMsg, LoaderControllerMsg> | null","comlinkProjectId: string | null","comlinkDataset: string | null"],"sources":["../../src/live/hooks/context.ts","../../src/live/hooks/useDraftMode.ts","../../src/live/hooks/useIsPresentationTool.ts","../../src/live/hooks/useIsLivePreview.ts","../../src/live/hooks/usePresentationQuery.ts"],"sourcesContent":[],"mappings":";;;;;;AAWA;AAmBA;;ACTA;AAmBgB,KD7BJ,gBAAA,GC6BI,UAAA,GAA2B,SAAA,GD7Ba,mBC6Bb;AGjB3C;;;;AAGe,KJIH,gBAAA,GIJG,UAAA,GAAA,qBAAA,GAAA,qBAAA,GAAA,MAAA,GAAA,QAAA,GAAA,SAAA;;;;;AJff;AAmBA;;ACTA;AAmBA;;AC5BgB,iBDSA,uBAAA,CAAA,CCTA,EDS2B,gBCT3B;;ACKhB;;ACDA;AAOA;;AACQ,iBHgBQ,uBAAA,CAAA,CGhBR,EHgBmC,gBGhBnC;;;;;;AJbR;AAmBA;;ACTA;AAmBA;iBC5BgB,qBAAA,CAAA;;;;;;AFDhB;AAmBA;;ACTA;AAmBA;;AC5BA;;ACKA;;ACDY,iBDCI,gBAAA,CAAA,CCDJ,EAAA,OAAA,GAAA,IAAA;;KAAA,mCAAA;;;EJLZ,WAAY,EAAA,IAAA;AAmBZ,CAAA;;ACTgB,KGEJ,iCHF+B,CAAA,oBAAA,MAAA,CAAA,GAAA;EAmB3C,IAAgB,EGhBR,YHgBQ,CGhBK,WHgBL,CAAA;aGfH;EFbb,WAAgB,EEcD,mBFdC;;ACKA,KCYJ,2BDZI,CAAA,oBAAA,MAAA,CAAA,GCaZ,mCDbY,GCcZ,iCDdY,CCcsB,WDdtB,CAAA;;ACDhB;AAOA;;;;;;AAMY,iBAyDI,oBAzDJ,CAAA,0BAAA,MAAA,CAAA,CAAA,KAAA,EAAA;EACR,KAAA,EAyDK,WAzDL;EACkC,MAAA,CAAA,EAyD3B,WAzD2B,GAyDb,OAzDa,CAyDL,WAzDK,CAAA;EAAlC,KAAA,CAAA,EAAA,OAAA;CAAA,CAAA,EA2DA,2BA3DA,CA2D4B,WA3D5B,CAAA"}
|
package/dist/hooks/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["environment","environment","initialState: UsePresentationQueryReturnsInactive","EMPTY_QUERY_PARAMS: QueryParams","comlink","comlinkSnapshot","perspective"],"sources":["../../src/live/hooks/useDraftMode.ts","../../src/live/hooks/useIsPresentationTool.ts","../../src/live/hooks/useIsLivePreview.ts","../../src/live/hooks/usePresentationQuery.ts"],"sourcesContent":["import {useCallback, useSyncExternalStore} from 'react'\nimport {\n environment,\n environmentListeners,\n perspective,\n perspectiveListeners,\n type DraftEnvironment,\n type DraftPerspective,\n} from './context'\n\n/**\n * Reports the current draft mode environment.\n * Use it to determine how to adapt the UI based on wether:\n * - Your app is previewed in a iframe, inside Presentation Tool in a Sanity Studio.\n * - Your app is previewed in a new window, spawned from Presentation Tool in a Sanity Studio.\n * - Your app is live previewing drafts in a standalone context.\n * - Your app is previewing drafts, but not live.\n * - Your app is not previewing anything (that could be detected).\n * @public\n */\nexport function useDraftModeEnvironment(): DraftEnvironment {\n const subscribe = useCallback((listener: () => void) => {\n environmentListeners.add(listener)\n return () => environmentListeners.delete(listener)\n }, [])\n\n return useSyncExternalStore(\n subscribe,\n () => environment,\n () => 'checking',\n )\n}\n\n/**\n * Reports the Sanity Client perspective used to fetch data in `sanityFetch` used on the page.\n * If the hook is used outside Draft Mode it will resolve to `'unknown'`.\n * If the hook is used but the `<SanityLive />` component is not present then it'll stay in `'checking'` and console warn after a timeout that it seems like you're missing the component.\n * @public\n */\nexport function useDraftModePerspective(): DraftPerspective {\n const subscribe = useCallback((listener: () => void) => {\n perspectiveListeners.add(listener)\n return () => perspectiveListeners.delete(listener)\n }, [])\n\n return useSyncExternalStore(\n subscribe,\n () => perspective,\n () => 'checking',\n )\n}\n","import {useDraftModeEnvironment} from './useDraftMode'\n\n/**\n * Detects if the application is being previewed inside Sanity Presentation Tool.\n * Presentation Tool can open the application in an iframe, or in a new window.\n * When in this context there are some UI you usually don't want to show,\n * for example a Draft Mode toggle, or a \"Viewing draft content\" indicators, these are unnecessary and add clutter to\n * the editorial experience.\n * The hook returns `null` initially, when it's not yet sure if the application is running inside Presentation Tool,\n * then `true` if it is, and `false` otherwise.\n * @public\n */\nexport function useIsPresentationTool(): boolean | null {\n const environment = useDraftModeEnvironment()\n return environment === 'checking'\n ? null\n : environment === 'presentation-iframe' || environment === 'presentation-window'\n}\n","import {useDraftModeEnvironment} from './useDraftMode'\n\n/**\n * Detects if the application is considered to be in a \"Live Preview\" mode.\n * Live Preview means that the application is either:\n * - being previewed inside Sanity Presentation Tool\n * - being previewed in Draft Mode, with a `browserToken` given to `defineLive`, also known as \"Standalone Live Preview'\"\n * When in Live Preview mode, you typically want UI to update as new content comes in, without any manual intervention.\n * This is very different from Live Production mode, where you usually want to delay updates that might cause layout shifts,\n * to avoid interrupting the user that is consuming your content.\n * This hook lets you adapt to this difference, making sure production doesn't cause layout shifts that worsen the UX,\n * while in Live Preview mode layout shift is less of an issue and it's better for the editorial experience to auto refresh in real time.\n *\n * The hook returns `null` initially, to signal it doesn't yet know if it's live previewing or not.\n * Then `true` if it is, and `false` otherwise.\n * @public\n */\nexport function useIsLivePreview(): boolean | null {\n const environment = useDraftModeEnvironment()\n return environment === 'checking'\n ? null\n : environment === 'presentation-iframe' ||\n environment === 'presentation-window' ||\n environment === 'live'\n}\n","import type {ClientPerspective, ClientReturn, ContentSourceMap, QueryParams} from '@sanity/client'\nimport {stegaEncodeSourceMap} from '@sanity/client/stega'\nimport type {LoaderControllerMsg} from '@sanity/presentation-comlink'\nimport {dequal} from 'dequal/lite'\nimport {useEffect, useMemo, useReducer, useSyncExternalStore, useEffectEvent} from 'react'\nimport {\n comlinkDataset,\n comlinkListeners,\n comlinkProjectId,\n comlink as comlinkSnapshot,\n} from './context'\nimport {useDraftModePerspective} from './useDraftMode'\n\n/** @alpha */\nexport type UsePresentationQueryReturnsInactive = {\n data: null\n sourceMap: null\n perspective: null\n}\n\n/** @alpha */\nexport type UsePresentationQueryReturnsActive<QueryString extends string> = {\n data: ClientReturn<QueryString>\n sourceMap: ContentSourceMap | null\n perspective: ClientPerspective\n}\n\nexport type UsePresentationQueryReturns<QueryString extends string> =\n | UsePresentationQueryReturnsInactive\n | UsePresentationQueryReturnsActive<QueryString>\n\ntype Action<QueryString extends string> = {\n type: 'query-change'\n payload: UsePresentationQueryReturnsActive<QueryString>\n}\n\nfunction reducer<QueryString extends string>(\n state: UsePresentationQueryReturns<QueryString>,\n {type, payload}: Action<QueryString>,\n): UsePresentationQueryReturns<QueryString> {\n switch (type) {\n case 'query-change':\n return dequal(state, payload)\n ? state\n : {\n ...state,\n data: dequal(state.data, payload.data)\n ? // oxlint-disable-next-line no-unsafe-type-assertion\n (state.data as ClientReturn<QueryString>)\n : payload.data,\n sourceMap: dequal(state.sourceMap, payload.sourceMap)\n ? state.sourceMap\n : payload.sourceMap,\n perspective: dequal(state.perspective, payload.perspective)\n ? // oxlint-disable-next-line no-unsafe-type-assertion\n (state.perspective as Exclude<ClientPerspective, 'raw'>)\n : payload.perspective,\n }\n default:\n return state\n }\n}\nconst initialState: UsePresentationQueryReturnsInactive = {\n data: null,\n sourceMap: null,\n perspective: null,\n}\n\nfunction subscribe(listener: () => void) {\n comlinkListeners.add(listener)\n return () => comlinkListeners.delete(listener)\n}\n\nconst EMPTY_QUERY_PARAMS: QueryParams = {}\nconst LISTEN_HEARTBEAT_INTERVAL = 10_000\n\n/**\n * Experimental hook that can run queries in Presentation Tool.\n * Query results are sent back over postMessage whenever the query results change.\n * It also works with optimistic updates in the studio itself, offering low latency updates.\n * It's not as low latency as the `useOptimistic` hook, but it's a good compromise for some use cases.\n * Especially until `useOptimistic` propagates edits in the Studio parent window back into the iframe.\n * @alpha\n */\nexport function usePresentationQuery<const QueryString extends string>(props: {\n query: QueryString\n params?: QueryParams | Promise<QueryParams>\n stega?: boolean\n}): UsePresentationQueryReturns<QueryString> {\n const [state, dispatch] = useReducer(reducer, initialState)\n const {query, params = EMPTY_QUERY_PARAMS, stega = true} = props\n\n /**\n * Comlink forwards queries we want to run to the parent window where Presentation Tool handles it for us\n */\n const comlink = useSyncExternalStore(\n subscribe,\n () => comlinkSnapshot,\n () => null,\n )\n /**\n * The comlink events requires projectId and dataset, Presentation Tool uses it to protect against project and dataset mismatch errors.\n * We don't want to force the consumers of the `usePresentationQuery` hook to provide these,\n * so we set them in the component that establishes the comlink connection and propagates it to all the subscribes.\n */\n const projectId = useSyncExternalStore(\n subscribe,\n () => comlinkProjectId,\n () => null,\n )\n const dataset = useSyncExternalStore(\n subscribe,\n () => comlinkDataset,\n () => null,\n )\n /**\n * The perspective is kept in sync with Presentation Tool's perspective, and even knows what perspective the page loaded with initially and can forward it to the Sanity Studio.\n */\n const perspective = useDraftModePerspective()\n const handleQueryHeartbeat = useEffectEvent((comlink: NonNullable<typeof comlinkSnapshot>) => {\n // Handle odd case where the comlink can take events but some data is missing\n if (!projectId || !dataset || !perspective) {\n console.warn('usePresentationQuery: projectId, dataset and perspective must be set', {\n projectId,\n dataset,\n perspective,\n })\n return\n }\n // Another odd case where the initial perspective states haven't resolved to the actual perspective state\n if (perspective === 'checking' || perspective === 'unknown') {\n return\n }\n comlink.post('loader/query-listen', {\n projectId,\n dataset,\n perspective,\n query,\n params,\n heartbeat: LISTEN_HEARTBEAT_INTERVAL,\n })\n })\n const handleQueryChange = useEffectEvent(\n (event: Extract<LoaderControllerMsg, {type: 'loader/query-change'}>['data']) => {\n if (\n dequal(\n {\n projectId,\n dataset,\n query,\n params,\n },\n {\n projectId: event.projectId,\n dataset: event.dataset,\n query: event.query,\n params: event.params,\n },\n )\n ) {\n dispatch({\n type: 'query-change',\n payload: {\n data: event.result,\n sourceMap: event.resultSourceMap || null,\n perspective: event.perspective,\n },\n })\n }\n },\n )\n useEffect(() => {\n if (!comlink) return\n\n const unsubscribe = comlink.on('loader/query-change', handleQueryChange)\n const interval = setInterval(() => handleQueryHeartbeat(comlink), LISTEN_HEARTBEAT_INTERVAL)\n return () => {\n clearInterval(interval)\n unsubscribe()\n }\n }, [comlink])\n\n return useMemo(() => {\n if (stega && state.sourceMap) {\n return {\n ...state,\n data: stegaEncodeSourceMap(state.data, state.sourceMap, {enabled: true, studioUrl: '/'}),\n }\n }\n return state\n }, [state, stega])\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["environment","environment","initialState: UsePresentationQueryReturnsInactive","EMPTY_QUERY_PARAMS: QueryParams","comlink","comlinkSnapshot","perspective"],"sources":["../../src/live/hooks/useDraftMode.ts","../../src/live/hooks/useIsPresentationTool.ts","../../src/live/hooks/useIsLivePreview.ts","../../src/live/hooks/usePresentationQuery.ts"],"sourcesContent":["import {useCallback, useSyncExternalStore} from 'react'\n\nimport {\n environment,\n environmentListeners,\n perspective,\n perspectiveListeners,\n type DraftEnvironment,\n type DraftPerspective,\n} from './context'\n\n/**\n * Reports the current draft mode environment.\n * Use it to determine how to adapt the UI based on wether:\n * - Your app is previewed in a iframe, inside Presentation Tool in a Sanity Studio.\n * - Your app is previewed in a new window, spawned from Presentation Tool in a Sanity Studio.\n * - Your app is live previewing drafts in a standalone context.\n * - Your app is previewing drafts, but not live.\n * - Your app is not previewing anything (that could be detected).\n * @public\n */\nexport function useDraftModeEnvironment(): DraftEnvironment {\n const subscribe = useCallback((listener: () => void) => {\n environmentListeners.add(listener)\n return () => environmentListeners.delete(listener)\n }, [])\n\n return useSyncExternalStore(\n subscribe,\n () => environment,\n () => 'checking',\n )\n}\n\n/**\n * Reports the Sanity Client perspective used to fetch data in `sanityFetch` used on the page.\n * If the hook is used outside Draft Mode it will resolve to `'unknown'`.\n * If the hook is used but the `<SanityLive />` component is not present then it'll stay in `'checking'` and console warn after a timeout that it seems like you're missing the component.\n * @public\n */\nexport function useDraftModePerspective(): DraftPerspective {\n const subscribe = useCallback((listener: () => void) => {\n perspectiveListeners.add(listener)\n return () => perspectiveListeners.delete(listener)\n }, [])\n\n return useSyncExternalStore(\n subscribe,\n () => perspective,\n () => 'checking',\n )\n}\n","import {useDraftModeEnvironment} from './useDraftMode'\n\n/**\n * Detects if the application is being previewed inside Sanity Presentation Tool.\n * Presentation Tool can open the application in an iframe, or in a new window.\n * When in this context there are some UI you usually don't want to show,\n * for example a Draft Mode toggle, or a \"Viewing draft content\" indicators, these are unnecessary and add clutter to\n * the editorial experience.\n * The hook returns `null` initially, when it's not yet sure if the application is running inside Presentation Tool,\n * then `true` if it is, and `false` otherwise.\n * @public\n */\nexport function useIsPresentationTool(): boolean | null {\n const environment = useDraftModeEnvironment()\n return environment === 'checking'\n ? null\n : environment === 'presentation-iframe' || environment === 'presentation-window'\n}\n","import {useDraftModeEnvironment} from './useDraftMode'\n\n/**\n * Detects if the application is considered to be in a \"Live Preview\" mode.\n * Live Preview means that the application is either:\n * - being previewed inside Sanity Presentation Tool\n * - being previewed in Draft Mode, with a `browserToken` given to `defineLive`, also known as \"Standalone Live Preview'\"\n * When in Live Preview mode, you typically want UI to update as new content comes in, without any manual intervention.\n * This is very different from Live Production mode, where you usually want to delay updates that might cause layout shifts,\n * to avoid interrupting the user that is consuming your content.\n * This hook lets you adapt to this difference, making sure production doesn't cause layout shifts that worsen the UX,\n * while in Live Preview mode layout shift is less of an issue and it's better for the editorial experience to auto refresh in real time.\n *\n * The hook returns `null` initially, to signal it doesn't yet know if it's live previewing or not.\n * Then `true` if it is, and `false` otherwise.\n * @public\n */\nexport function useIsLivePreview(): boolean | null {\n const environment = useDraftModeEnvironment()\n return environment === 'checking'\n ? null\n : environment === 'presentation-iframe' ||\n environment === 'presentation-window' ||\n environment === 'live'\n}\n","import type {ClientPerspective, ClientReturn, ContentSourceMap, QueryParams} from '@sanity/client'\nimport type {LoaderControllerMsg} from '@sanity/presentation-comlink'\n\nimport {stegaEncodeSourceMap} from '@sanity/client/stega'\nimport {dequal} from 'dequal/lite'\nimport {useEffect, useMemo, useReducer, useSyncExternalStore, useEffectEvent} from 'react'\n\nimport {\n comlinkDataset,\n comlinkListeners,\n comlinkProjectId,\n comlink as comlinkSnapshot,\n} from './context'\nimport {useDraftModePerspective} from './useDraftMode'\n\n/** @alpha */\nexport type UsePresentationQueryReturnsInactive = {\n data: null\n sourceMap: null\n perspective: null\n}\n\n/** @alpha */\nexport type UsePresentationQueryReturnsActive<QueryString extends string> = {\n data: ClientReturn<QueryString>\n sourceMap: ContentSourceMap | null\n perspective: ClientPerspective\n}\n\nexport type UsePresentationQueryReturns<QueryString extends string> =\n | UsePresentationQueryReturnsInactive\n | UsePresentationQueryReturnsActive<QueryString>\n\ntype Action<QueryString extends string> = {\n type: 'query-change'\n payload: UsePresentationQueryReturnsActive<QueryString>\n}\n\nfunction reducer<QueryString extends string>(\n state: UsePresentationQueryReturns<QueryString>,\n {type, payload}: Action<QueryString>,\n): UsePresentationQueryReturns<QueryString> {\n switch (type) {\n case 'query-change':\n return dequal(state, payload)\n ? state\n : {\n ...state,\n data: dequal(state.data, payload.data)\n ? // oxlint-disable-next-line no-unsafe-type-assertion\n (state.data as ClientReturn<QueryString>)\n : payload.data,\n sourceMap: dequal(state.sourceMap, payload.sourceMap)\n ? state.sourceMap\n : payload.sourceMap,\n perspective: dequal(state.perspective, payload.perspective)\n ? // oxlint-disable-next-line no-unsafe-type-assertion\n (state.perspective as Exclude<ClientPerspective, 'raw'>)\n : payload.perspective,\n }\n default:\n return state\n }\n}\nconst initialState: UsePresentationQueryReturnsInactive = {\n data: null,\n sourceMap: null,\n perspective: null,\n}\n\nfunction subscribe(listener: () => void) {\n comlinkListeners.add(listener)\n return () => comlinkListeners.delete(listener)\n}\n\nconst EMPTY_QUERY_PARAMS: QueryParams = {}\nconst LISTEN_HEARTBEAT_INTERVAL = 10_000\n\n/**\n * Experimental hook that can run queries in Presentation Tool.\n * Query results are sent back over postMessage whenever the query results change.\n * It also works with optimistic updates in the studio itself, offering low latency updates.\n * It's not as low latency as the `useOptimistic` hook, but it's a good compromise for some use cases.\n * Especially until `useOptimistic` propagates edits in the Studio parent window back into the iframe.\n * @alpha\n */\nexport function usePresentationQuery<const QueryString extends string>(props: {\n query: QueryString\n params?: QueryParams | Promise<QueryParams>\n stega?: boolean\n}): UsePresentationQueryReturns<QueryString> {\n const [state, dispatch] = useReducer(reducer, initialState)\n const {query, params = EMPTY_QUERY_PARAMS, stega = true} = props\n\n /**\n * Comlink forwards queries we want to run to the parent window where Presentation Tool handles it for us\n */\n const comlink = useSyncExternalStore(\n subscribe,\n () => comlinkSnapshot,\n () => null,\n )\n /**\n * The comlink events requires projectId and dataset, Presentation Tool uses it to protect against project and dataset mismatch errors.\n * We don't want to force the consumers of the `usePresentationQuery` hook to provide these,\n * so we set them in the component that establishes the comlink connection and propagates it to all the subscribes.\n */\n const projectId = useSyncExternalStore(\n subscribe,\n () => comlinkProjectId,\n () => null,\n )\n const dataset = useSyncExternalStore(\n subscribe,\n () => comlinkDataset,\n () => null,\n )\n /**\n * The perspective is kept in sync with Presentation Tool's perspective, and even knows what perspective the page loaded with initially and can forward it to the Sanity Studio.\n */\n const perspective = useDraftModePerspective()\n const handleQueryHeartbeat = useEffectEvent((comlink: NonNullable<typeof comlinkSnapshot>) => {\n // Handle odd case where the comlink can take events but some data is missing\n if (!projectId || !dataset || !perspective) {\n console.warn('usePresentationQuery: projectId, dataset and perspective must be set', {\n projectId,\n dataset,\n perspective,\n })\n return\n }\n // Another odd case where the initial perspective states haven't resolved to the actual perspective state\n if (perspective === 'checking' || perspective === 'unknown') {\n return\n }\n comlink.post('loader/query-listen', {\n projectId,\n dataset,\n perspective,\n query,\n params,\n heartbeat: LISTEN_HEARTBEAT_INTERVAL,\n })\n })\n const handleQueryChange = useEffectEvent(\n (event: Extract<LoaderControllerMsg, {type: 'loader/query-change'}>['data']) => {\n if (\n dequal(\n {\n projectId,\n dataset,\n query,\n params,\n },\n {\n projectId: event.projectId,\n dataset: event.dataset,\n query: event.query,\n params: event.params,\n },\n )\n ) {\n dispatch({\n type: 'query-change',\n payload: {\n data: event.result,\n sourceMap: event.resultSourceMap || null,\n perspective: event.perspective,\n },\n })\n }\n },\n )\n useEffect(() => {\n if (!comlink) return\n\n const unsubscribe = comlink.on('loader/query-change', handleQueryChange)\n const interval = setInterval(() => handleQueryHeartbeat(comlink), LISTEN_HEARTBEAT_INTERVAL)\n return () => {\n clearInterval(interval)\n unsubscribe()\n }\n }, [comlink])\n\n return useMemo(() => {\n if (stega && state.sourceMap) {\n return {\n ...state,\n data: stegaEncodeSourceMap(state.data, state.sourceMap, {enabled: true, studioUrl: '/'}),\n }\n }\n return state\n }, [state, stega])\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../../src/live/client-components/live/SanityLive.tsx"],"sourcesContent":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../../src/live/client-components/live/SanityLive.tsx"],"sourcesContent":[],"mappings":";;AAyBA;;AAYyB,UAZR,eAAA,SAAwB,IAYhB,CAXvB,uBAWuB,EAAA,WAAA,GAAA,SAAA,GAAA,SAAA,GAAA,YAAA,GAAA,oBAAA,GAAA,OAAA,GAAA,kBAAA,CAAA,CAAA;EAWJ,gBAAA,EAAA,OAAA;EACS,oBAAA,CAAA,EAZL,iBAYK;EAAc,cAAA,CAAA,EAAA,OAAA;EAxBH,cAAA,CAAA,EAAA,OAAA;EAAA,kBAAA,CAAA,EAAA,OAAA;EA0DzC,UAAgB,EAAA,MAAA,GAAA,SAAkB;;;;;;;qBAnCb;8BACS,cAAc;;;;;iBAkC5B,UAAA,QAAkB,kBAAkB,KAAA,CAAM,GAAA,CAAI"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["defaultRevalidateSyncTags","revalidateSyncTags"],"sources":["../../../../src/live/client-components/live/SanityLive.tsx"],"sourcesContent":["import {\n createClient,\n type ClientPerspective,\n type InitializedClientConfig,\n type LiveEvent,\n type LiveEventGoAway,\n type SyncTag,\n} from '@sanity/client'\nimport {revalidateSyncTags as defaultRevalidateSyncTags} from 'next-sanity/live/server-actions'\nimport {isMaybePresentation, isMaybePreviewWindow} from '@sanity/presentation-comlink'\nimport dynamic from 'next/dynamic'\nimport {useRouter} from 'next/navigation'\nimport {useEffect, useMemo, useRef, useState, useEffectEvent} from 'react'\nimport {setEnvironment, setPerspective} from '../../hooks/context'\nimport {isCorsOriginError} from '../../../isCorsOriginError'\n\nconst PresentationComlink = dynamic(() => import('./PresentationComlink'), {ssr: false})\nconst RefreshOnMount = dynamic(() => import('./RefreshOnMount'), {ssr: false})\nconst RefreshOnFocus = dynamic(() => import('./RefreshOnFocus'), {ssr: false})\nconst RefreshOnReconnect = dynamic(() => import('./RefreshOnReconnect'), {ssr: false})\n\n/**\n * @public\n */\nexport interface SanityLiveProps extends Pick<\n InitializedClientConfig,\n | 'projectId'\n | 'dataset'\n | 'apiHost'\n | 'apiVersion'\n | 'useProjectHostname'\n | 'token'\n | 'requestTagPrefix'\n> {\n // handleDraftModeAction: (secret: string) => Promise<void | string>\n draftModeEnabled: boolean\n draftModePerspective?: ClientPerspective\n refreshOnMount?: boolean\n refreshOnFocus?: boolean\n refreshOnReconnect?: boolean\n requestTag: string | undefined\n /**\n * Handle errors from the Live Events subscription.\n * By default it's reported using `console.error`, you can override this prop to handle it in your own way.\n */\n onError?: (error: unknown) => void\n intervalOnGoAway?: number | false\n onGoAway?: (event: LiveEventGoAway, intervalOnGoAway: number | false) => void\n revalidateSyncTags?: (tags: SyncTag[]) => Promise<void | 'refresh'>\n}\n\nfunction handleError(error: unknown) {\n /* eslint-disable no-console */\n if (isCorsOriginError(error)) {\n console.warn(\n `Sanity Live is unable to connect to the Sanity API as the current origin - ${window.origin} - is not in the list of allowed CORS origins for this Sanity Project.`,\n error.addOriginUrl && `Add it here:`,\n error.addOriginUrl?.toString(),\n )\n } else {\n console.error(error)\n }\n /* eslint-enable no-console */\n}\n\nfunction handleOnGoAway(event: LiveEventGoAway, intervalOnGoAway: number | false) {\n /* eslint-disable no-console */\n if (intervalOnGoAway) {\n console.warn(\n 'Sanity Live connection closed, switching to long polling set to a interval of',\n intervalOnGoAway / 1000,\n 'seconds and the server gave this reason:',\n event.reason,\n )\n } else {\n console.error(\n 'Sanity Live connection closed, automatic revalidation is disabled, the server gave this reason:',\n event.reason,\n )\n }\n /* eslint-enable no-console */\n}\n\n/**\n * @public\n */\nexport function SanityLive(props: SanityLiveProps): React.JSX.Element | null {\n const {\n projectId,\n dataset,\n apiHost,\n apiVersion,\n useProjectHostname,\n token,\n requestTagPrefix,\n // handleDraftModeAction,\n draftModeEnabled,\n draftModePerspective,\n refreshOnMount = false,\n refreshOnFocus = draftModeEnabled\n ? false\n : typeof window === 'undefined'\n ? true\n : window.self === window.top,\n refreshOnReconnect = true,\n intervalOnGoAway = 30_000,\n requestTag = 'next-loader.live',\n onError = handleError,\n onGoAway = handleOnGoAway,\n revalidateSyncTags = defaultRevalidateSyncTags,\n } = props\n\n const client = useMemo(\n () =>\n createClient({\n projectId,\n dataset,\n apiHost,\n apiVersion,\n useProjectHostname,\n ignoreBrowserTokenWarning: true,\n token,\n useCdn: false,\n requestTagPrefix,\n }),\n [apiHost, apiVersion, dataset, projectId, requestTagPrefix, token, useProjectHostname],\n )\n const [longPollingInterval, setLongPollingInterval] = useState<number | false>(false)\n\n /**\n * 1. Handle Live Events and call revalidateTag or router.refresh when needed\n */\n const router = useRouter()\n const handleLiveEvent = useEffectEvent((event: LiveEvent) => {\n if (process.env.NODE_ENV !== 'production' && event.type === 'welcome') {\n // eslint-disable-next-line no-console\n console.info(\n 'Sanity is live with',\n token\n ? 'automatic revalidation for draft content changes as well as published content'\n : draftModeEnabled\n ? 'automatic revalidation for only published content. Provide a `browserToken` to `defineLive` to support draft content outside of Presentation Tool.'\n : 'automatic revalidation of published content',\n )\n // Disable long polling when welcome event is received, this is a no-op if long polling is already disabled\n setLongPollingInterval(false)\n } else if (event.type === 'message') {\n void revalidateSyncTags(event.tags).then((result) => {\n if (result === 'refresh') router.refresh()\n })\n } else if (event.type === 'restart' || event.type === 'reconnect') {\n router.refresh()\n } else if (event.type === 'goaway') {\n onGoAway(event, intervalOnGoAway)\n setLongPollingInterval(intervalOnGoAway)\n }\n })\n useEffect(() => {\n const subscription = client.live.events({includeDrafts: !!token, tag: requestTag}).subscribe({\n next: handleLiveEvent,\n error: (err: unknown) => {\n // console.error('What?', err)\n onError(err)\n },\n })\n return () => subscription.unsubscribe()\n }, [client.live, onError, requestTag, token])\n\n /**\n * 2. Notify what perspective we're in, when in Draft Mode\n */\n useEffect(() => {\n if (draftModeEnabled && draftModePerspective) {\n setPerspective(draftModePerspective)\n } else {\n setPerspective('unknown')\n }\n }, [draftModeEnabled, draftModePerspective])\n\n const [loadComlink, setLoadComlink] = useState(false)\n /**\n * 3. Notify what environment we're in, when in Draft Mode\n */\n useEffect(() => {\n // If we might be in Presentation Tool, then skip detecting here as it's handled later\n if (isMaybePresentation()) return\n\n // If we're definitely not in Presentation Tool, then we can set the environment as stand-alone live preview\n // if we have both a browser token, and draft mode is enabled\n if (draftModeEnabled && token) {\n setEnvironment('live')\n return\n }\n // If we're in draft mode, but don't have a browser token, then we're in static mode\n // which means that published content is still live, but draft changes likely need manual refresh\n if (draftModeEnabled) {\n setEnvironment('static')\n return\n }\n\n // Fallback to `unknown` otherwise, as we simply don't know how it's setup\n setEnvironment('unknown')\n return\n }, [draftModeEnabled, token])\n\n /**\n * 4. If Presentation Tool is detected, load up the comlink and integrate with it\n */\n useEffect(() => {\n if (!isMaybePresentation()) return\n const controller = new AbortController()\n // Wait for a while to see if Presentation Tool is detected, before assuming the env to be stand-alone live preview\n const timeout = setTimeout(() => setEnvironment('live'), 3_000)\n window.addEventListener(\n 'message',\n ({data}: MessageEvent<unknown>) => {\n if (\n data &&\n typeof data === 'object' &&\n 'domain' in data &&\n data.domain === 'sanity/channels' &&\n 'from' in data &&\n data.from === 'presentation'\n ) {\n clearTimeout(timeout)\n setEnvironment(isMaybePreviewWindow() ? 'presentation-window' : 'presentation-iframe')\n setLoadComlink(true)\n controller.abort()\n }\n },\n {signal: controller.signal},\n )\n return () => {\n clearTimeout(timeout)\n controller.abort()\n }\n }, [])\n\n /**\n * 5. Warn if draft mode is being disabled\n * @TODO move logic into PresentationComlink, or maybe VisualEditing?\n */\n const draftModeEnabledWarnRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n useEffect(() => {\n if (!draftModeEnabled) return\n clearTimeout(draftModeEnabledWarnRef.current)\n return () => {\n draftModeEnabledWarnRef.current = setTimeout(() => {\n // eslint-disable-next-line no-console\n console.warn('Sanity Live: Draft mode was enabled, but is now being disabled')\n })\n }\n }, [draftModeEnabled])\n\n /**\n * 6. Handle switching to long polling when needed\n */\n useEffect(() => {\n if (!longPollingInterval) return\n const interval = setInterval(() => router.refresh(), longPollingInterval)\n return () => clearInterval(interval)\n }, [longPollingInterval, router])\n\n return (\n <>\n {draftModeEnabled && loadComlink && (\n <PresentationComlink\n projectId={projectId!}\n dataset={dataset!}\n // handleDraftModeAction={handleDraftModeAction}\n draftModeEnabled={draftModeEnabled}\n draftModePerspective={draftModePerspective!}\n />\n )}\n {!draftModeEnabled && refreshOnMount && <RefreshOnMount />}\n {!draftModeEnabled && refreshOnFocus && <RefreshOnFocus />}\n {!draftModeEnabled && refreshOnReconnect && <RefreshOnReconnect />}\n </>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["defaultRevalidateSyncTags","revalidateSyncTags"],"sources":["../../../../src/live/client-components/live/SanityLive.tsx"],"sourcesContent":["import {\n createClient,\n type ClientPerspective,\n type InitializedClientConfig,\n type LiveEvent,\n type LiveEventGoAway,\n type SyncTag,\n} from '@sanity/client'\nimport {isMaybePresentation, isMaybePreviewWindow} from '@sanity/presentation-comlink'\nimport {revalidateSyncTags as defaultRevalidateSyncTags} from 'next-sanity/live/server-actions'\nimport dynamic from 'next/dynamic'\nimport {useRouter} from 'next/navigation'\nimport {useEffect, useMemo, useRef, useState, useEffectEvent} from 'react'\n\nimport {isCorsOriginError} from '../../../isCorsOriginError'\nimport {setEnvironment, setPerspective} from '../../hooks/context'\n\nconst PresentationComlink = dynamic(() => import('./PresentationComlink'), {ssr: false})\nconst RefreshOnMount = dynamic(() => import('./RefreshOnMount'), {ssr: false})\nconst RefreshOnFocus = dynamic(() => import('./RefreshOnFocus'), {ssr: false})\nconst RefreshOnReconnect = dynamic(() => import('./RefreshOnReconnect'), {ssr: false})\n\n/**\n * @public\n */\nexport interface SanityLiveProps extends Pick<\n InitializedClientConfig,\n | 'projectId'\n | 'dataset'\n | 'apiHost'\n | 'apiVersion'\n | 'useProjectHostname'\n | 'token'\n | 'requestTagPrefix'\n> {\n // handleDraftModeAction: (secret: string) => Promise<void | string>\n draftModeEnabled: boolean\n draftModePerspective?: ClientPerspective\n refreshOnMount?: boolean\n refreshOnFocus?: boolean\n refreshOnReconnect?: boolean\n requestTag: string | undefined\n /**\n * Handle errors from the Live Events subscription.\n * By default it's reported using `console.error`, you can override this prop to handle it in your own way.\n */\n onError?: (error: unknown) => void\n intervalOnGoAway?: number | false\n onGoAway?: (event: LiveEventGoAway, intervalOnGoAway: number | false) => void\n revalidateSyncTags?: (tags: SyncTag[]) => Promise<void | 'refresh'>\n}\n\nfunction handleError(error: unknown) {\n if (isCorsOriginError(error)) {\n console.warn(\n `Sanity Live is unable to connect to the Sanity API as the current origin - ${window.origin} - is not in the list of allowed CORS origins for this Sanity Project.`,\n error.addOriginUrl && `Add it here:`,\n error.addOriginUrl?.toString(),\n )\n } else {\n console.error(error)\n }\n}\n\nfunction handleOnGoAway(event: LiveEventGoAway, intervalOnGoAway: number | false) {\n if (intervalOnGoAway) {\n console.warn(\n 'Sanity Live connection closed, switching to long polling set to a interval of',\n intervalOnGoAway / 1000,\n 'seconds and the server gave this reason:',\n event.reason,\n )\n } else {\n console.error(\n 'Sanity Live connection closed, automatic revalidation is disabled, the server gave this reason:',\n event.reason,\n )\n }\n}\n\n/**\n * @public\n */\nexport function SanityLive(props: SanityLiveProps): React.JSX.Element | null {\n const {\n projectId,\n dataset,\n apiHost,\n apiVersion,\n useProjectHostname,\n token,\n requestTagPrefix,\n // handleDraftModeAction,\n draftModeEnabled,\n draftModePerspective,\n refreshOnMount = false,\n refreshOnFocus = draftModeEnabled\n ? false\n : typeof window === 'undefined'\n ? true\n : window.self === window.top,\n refreshOnReconnect = true,\n intervalOnGoAway = 30_000,\n requestTag = 'next-loader.live',\n onError = handleError,\n onGoAway = handleOnGoAway,\n revalidateSyncTags = defaultRevalidateSyncTags,\n } = props\n\n const client = useMemo(\n () =>\n createClient({\n projectId,\n dataset,\n apiHost,\n apiVersion,\n useProjectHostname,\n ignoreBrowserTokenWarning: true,\n token,\n useCdn: false,\n requestTagPrefix,\n }),\n [apiHost, apiVersion, dataset, projectId, requestTagPrefix, token, useProjectHostname],\n )\n const [longPollingInterval, setLongPollingInterval] = useState<number | false>(false)\n\n /**\n * 1. Handle Live Events and call revalidateTag or router.refresh when needed\n */\n const router = useRouter()\n const handleLiveEvent = useEffectEvent((event: LiveEvent) => {\n if (process.env.NODE_ENV !== 'production' && event.type === 'welcome') {\n // oxlint-disable-next-line no-console\n console.info(\n 'Sanity is live with',\n token\n ? 'automatic revalidation for draft content changes as well as published content'\n : draftModeEnabled\n ? 'automatic revalidation for only published content. Provide a `browserToken` to `defineLive` to support draft content outside of Presentation Tool.'\n : 'automatic revalidation of published content',\n )\n // Disable long polling when welcome event is received, this is a no-op if long polling is already disabled\n setLongPollingInterval(false)\n } else if (event.type === 'message') {\n void revalidateSyncTags(event.tags).then((result) => {\n if (result === 'refresh') router.refresh()\n })\n } else if (event.type === 'restart' || event.type === 'reconnect') {\n router.refresh()\n } else if (event.type === 'goaway') {\n onGoAway(event, intervalOnGoAway)\n setLongPollingInterval(intervalOnGoAway)\n }\n })\n useEffect(() => {\n const subscription = client.live.events({includeDrafts: !!token, tag: requestTag}).subscribe({\n next: handleLiveEvent,\n error: (err: unknown) => {\n // console.error('What?', err)\n onError(err)\n },\n })\n return () => subscription.unsubscribe()\n }, [client.live, onError, requestTag, token])\n\n /**\n * 2. Notify what perspective we're in, when in Draft Mode\n */\n useEffect(() => {\n if (draftModeEnabled && draftModePerspective) {\n setPerspective(draftModePerspective)\n } else {\n setPerspective('unknown')\n }\n }, [draftModeEnabled, draftModePerspective])\n\n const [loadComlink, setLoadComlink] = useState(false)\n /**\n * 3. Notify what environment we're in, when in Draft Mode\n */\n useEffect(() => {\n // If we might be in Presentation Tool, then skip detecting here as it's handled later\n if (isMaybePresentation()) return\n\n // If we're definitely not in Presentation Tool, then we can set the environment as stand-alone live preview\n // if we have both a browser token, and draft mode is enabled\n if (draftModeEnabled && token) {\n setEnvironment('live')\n return\n }\n // If we're in draft mode, but don't have a browser token, then we're in static mode\n // which means that published content is still live, but draft changes likely need manual refresh\n if (draftModeEnabled) {\n setEnvironment('static')\n return\n }\n\n // Fallback to `unknown` otherwise, as we simply don't know how it's setup\n setEnvironment('unknown')\n return\n }, [draftModeEnabled, token])\n\n /**\n * 4. If Presentation Tool is detected, load up the comlink and integrate with it\n */\n useEffect(() => {\n if (!isMaybePresentation()) return\n const controller = new AbortController()\n // Wait for a while to see if Presentation Tool is detected, before assuming the env to be stand-alone live preview\n const timeout = setTimeout(() => setEnvironment('live'), 3_000)\n window.addEventListener(\n 'message',\n ({data}: MessageEvent<unknown>) => {\n if (\n data &&\n typeof data === 'object' &&\n 'domain' in data &&\n data.domain === 'sanity/channels' &&\n 'from' in data &&\n data.from === 'presentation'\n ) {\n clearTimeout(timeout)\n setEnvironment(isMaybePreviewWindow() ? 'presentation-window' : 'presentation-iframe')\n setLoadComlink(true)\n controller.abort()\n }\n },\n {signal: controller.signal},\n )\n return () => {\n clearTimeout(timeout)\n controller.abort()\n }\n }, [])\n\n /**\n * 5. Warn if draft mode is being disabled\n * @TODO move logic into PresentationComlink, or maybe VisualEditing?\n */\n const draftModeEnabledWarnRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n useEffect(() => {\n if (!draftModeEnabled) return\n clearTimeout(draftModeEnabledWarnRef.current)\n return () => {\n draftModeEnabledWarnRef.current = setTimeout(() => {\n console.warn('Sanity Live: Draft mode was enabled, but is now being disabled')\n })\n }\n }, [draftModeEnabled])\n\n /**\n * 6. Handle switching to long polling when needed\n */\n useEffect(() => {\n if (!longPollingInterval) return\n const interval = setInterval(() => router.refresh(), longPollingInterval)\n return () => clearInterval(interval)\n }, [longPollingInterval, router])\n\n return (\n <>\n {draftModeEnabled && loadComlink && (\n <PresentationComlink\n projectId={projectId!}\n dataset={dataset!}\n // handleDraftModeAction={handleDraftModeAction}\n draftModeEnabled={draftModeEnabled}\n draftModePerspective={draftModePerspective!}\n />\n )}\n {!draftModeEnabled && refreshOnMount && <RefreshOnMount />}\n {!draftModeEnabled && refreshOnFocus && <RefreshOnFocus />}\n {!draftModeEnabled && refreshOnReconnect && <RefreshOnReconnect />}\n </>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../../src/live/client-components/live-stream/SanityLiveStream.tsx","../../../../src/live/client-components/live-stream/SanityLiveStreamLazy.tsx"],"sourcesContent":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../../src/live/client-components/live-stream/SanityLiveStream.tsx","../../../../src/live/client-components/live-stream/SanityLiveStreamLazy.tsx"],"sourcesContent":[],"mappings":";;AAkBA;;AAKW,UALM,qBAAA,SAA8B,IAKpC,CAJT,uBAIS,EAAA,WAAA,GAAA,SAAA,CAAA,CAAA;EACa,KAAA,EAAA,MAAA;EAAR,MAAA,CAAA,EADL,WACK;EAEG,WAAM,CAAA,EAFT,OAES,CAFD,iBAEC,EAAA,KAAA,CAAA;EAAd,KAAA,CAAA,EAAA,OAAA;EAGI,OAAA,EAHJ,OAGI,CAHI,KAAA,CAAM,SAGV,CAAA;EAEC,QAAM,EAAA,CAAA,MAAA,EAAA;IAAd,IAAA,EAAA,OAAA;IAbuC,SAAA,EAWhC,gBAXgC,GAAA,IAAA;IAAA,IAAA,EAAA,MAAA,EAAA;QAavC,QAAQ,KAAA,CAAM;ACnBtB;iBAAgB,mCAAA,QAA2C,wBAAwB,KAAA,CAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/live/client-components/live-stream/SanityLiveStreamLazy.tsx"],"sourcesContent":["/**\n * This file works around a new restriction in Next v15 where server components are not allowed\n * to use dynamic(() => import('...), {ssr: false})\n * only Client Components can set ssr: false.\n */\n\nimport dynamic from 'next/dynamic'\nimport type {SanityLiveStreamProps} from './SanityLiveStream'\n\nconst SanityLiveStreamClientComponent = dynamic(() => import('./SanityLiveStream'), {ssr: false})\n\nexport function SanityLiveStreamLazyClientComponent(props: SanityLiveStreamProps): React.ReactNode {\n return <SanityLiveStreamClientComponent {...props} />\n}\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/live/client-components/live-stream/SanityLiveStreamLazy.tsx"],"sourcesContent":["/**\n * This file works around a new restriction in Next v15 where server components are not allowed\n * to use dynamic(() => import('...), {ssr: false})\n * only Client Components can set ssr: false.\n */\n\nimport dynamic from 'next/dynamic'\n\nimport type {SanityLiveStreamProps} from './SanityLiveStream'\n\nconst SanityLiveStreamClientComponent = dynamic(() => import('./SanityLiveStream'), {ssr: false})\n\nexport function SanityLiveStreamLazyClientComponent(props: SanityLiveStreamProps): React.ReactNode {\n return <SanityLiveStreamClientComponent {...props} />\n}\n"],"mappings":";;;AAUA,MAAM,kCAAkC,cAAc,OAAO,iCAAuB,EAAC,KAAK,OAAM,CAAC;AAEjG,SAAgB,oCAAoC,OAA+C;AACjG,QAAO,oBAAC,iCAAA,EAAgC,GAAI,OAAA,CAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/live/server-actions/index.ts"],"sourcesContent":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/live/server-actions/index.ts"],"sourcesContent":[],"mappings":";iBAUsB,kBAAA,OAAyB,YAAY;AAArC,iBAWA,oBAAA,CAXqC,WAAA,EAWH,iBAXG,CAAA,EAWiB,OAXjB,CAAA,IAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/live/server-actions/index.ts"],"sourcesContent":["'use server'\n\nimport type {ClientPerspective, SyncTag} from '@sanity/client'\nimport {perspectiveCookieName} from '@sanity/preview-url-secret/constants'\nimport {revalidateTag} from 'next/cache'\nimport {cookies, draftMode} from 'next/headers'\nimport {sanitizePerspective} from '../utils'\n\nexport async function revalidateSyncTags(tags: SyncTag[]): Promise<void> {\n revalidateTag('sanity:fetch-sync-tags', 'max')\n\n for (const _tag of tags) {\n const tag = `sanity:${_tag}`\n revalidateTag(tag, {expire: 0})\n console.log(`<SanityLive /> revalidated tag: ${tag}`)\n }\n}\n\nexport async function setPerspectiveCookie(perspective: ClientPerspective): Promise<void> {\n if (!(await draftMode()).isEnabled) {\n // throw new Error('Draft mode is not enabled, setting perspective cookie is not allowed')\n return\n }\n const sanitizedPerspective = sanitizePerspective(perspective, 'drafts')\n if (perspective !== sanitizedPerspective) {\n throw new Error(`Invalid perspective`, {cause: perspective})\n }\n\n ;(await cookies()).set(\n perspectiveCookieName,\n Array.isArray(sanitizedPerspective) ? sanitizedPerspective.join(',') : sanitizedPerspective,\n {\n httpOnly: true,\n path: '/',\n secure: true,\n sameSite: 'none',\n },\n )\n}\n"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/live/server-actions/index.ts"],"sourcesContent":["'use server'\n\nimport type {ClientPerspective, SyncTag} from '@sanity/client'\n\nimport {perspectiveCookieName} from '@sanity/preview-url-secret/constants'\nimport {revalidateTag} from 'next/cache'\nimport {cookies, draftMode} from 'next/headers'\n\nimport {sanitizePerspective} from '../utils'\n\nexport async function revalidateSyncTags(tags: SyncTag[]): Promise<void> {\n revalidateTag('sanity:fetch-sync-tags', 'max')\n\n for (const _tag of tags) {\n const tag = `sanity:${_tag}`\n revalidateTag(tag, {expire: 0})\n // oxlint-disable-next-line no-console\n console.log(`<SanityLive /> revalidated tag: ${tag}`)\n }\n}\n\nexport async function setPerspectiveCookie(perspective: ClientPerspective): Promise<void> {\n if (!(await draftMode()).isEnabled) {\n // throw new Error('Draft mode is not enabled, setting perspective cookie is not allowed')\n return\n }\n const sanitizedPerspective = sanitizePerspective(perspective, 'drafts')\n if (perspective !== sanitizedPerspective) {\n throw new Error(`Invalid perspective`, {cause: perspective})\n }\n\n ;(await cookies()).set(\n perspectiveCookieName,\n Array.isArray(sanitizedPerspective) ? sanitizedPerspective.join(',') : sanitizedPerspective,\n {\n httpOnly: true,\n path: '/',\n secure: true,\n sameSite: 'none',\n },\n )\n}\n"],"mappings":";;;;;AAUA,eAAsB,mBAAmB,MAAgC;AACvE,eAAc,0BAA0B,MAAM;AAE9C,MAAK,MAAM,QAAQ,MAAM;EACvB,MAAM,MAAM,UAAU;AACtB,gBAAc,KAAK,EAAC,QAAQ,GAAE,CAAC;AAE/B,UAAQ,IAAI,mCAAmC,MAAM;;;AAIzD,eAAsB,qBAAqB,aAA+C;AACxF,KAAI,EAAE,MAAM,WAAW,EAAE,UAEvB;CAEF,MAAM,uBAAuB,oBAAoB,aAAa,SAAS;AACvE,KAAI,gBAAgB,qBAClB,OAAM,IAAI,MAAM,uBAAuB,EAAC,OAAO,aAAY,CAAC;AAG7D,EAAC,MAAM,SAAS,EAAE,IACjB,uBACA,MAAM,QAAQ,qBAAqB,GAAG,qBAAqB,KAAK,IAAI,GAAG,sBACvE;EACE,UAAU;EACV,MAAM;EACN,QAAQ;EACR,UAAU;EACX,CACF"}
|
package/dist/live.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.js","names":["sanityFetch: DefinedSanityFetchType","sanityFetch","tag"],"sources":["../src/live/resolveCookiePerspective.ts","../src/live/defineLive.tsx"],"sourcesContent":["import type {ClientPerspective} from '@sanity/client'\nimport {perspectiveCookieName} from '@sanity/preview-url-secret/constants'\nimport {cookies, draftMode} from 'next/headers'\nimport {sanitizePerspective} from './utils'\n\n/**\n * @internal\n */\nexport async function resolveCookiePerspective(): Promise<Exclude<ClientPerspective, 'raw'>> {\n return (await draftMode()).isEnabled\n ? (await cookies()).has(perspectiveCookieName)\n ? sanitizePerspective((await cookies()).get(perspectiveCookieName)?.value, 'drafts')\n : 'drafts'\n : 'published'\n}\n","import {prefetchDNS, preconnect} from 'react-dom'\nimport {\n type ClientPerspective,\n type ClientReturn,\n type ContentSourceMap,\n type LiveEventGoAway,\n type QueryParams,\n type SanityClient,\n type SyncTag,\n} from '@sanity/client'\nimport SanityLiveClientComponent from 'next-sanity/live/client-components/live'\nimport SanityLiveStreamClientComponent from 'next-sanity/live/client-components/live-stream'\nimport {draftMode} from 'next/headers'\nimport {resolveCookiePerspective} from './resolveCookiePerspective'\n\n/**\n * @public\n */\nexport type DefinedSanityFetchType = <const QueryString extends string>(options: {\n query: QueryString\n params?: QueryParams | Promise<QueryParams>\n /**\n * Add custom `next.tags` to the underlying fetch request.\n * @see https://nextjs.org/docs/app/api-reference/functions/fetch#optionsnexttags\n * This can be used in conjunction with custom fallback revalidation strategies, as well as with custom Server Actions that mutate data and want to render with fresh data right away (faster than the Live Event latency).\n * @defaultValue `['sanity']`\n */\n tags?: string[]\n perspective?: Exclude<ClientPerspective, 'raw'>\n stega?: boolean\n /**\n * @deprecated use `requestTag` instead\n */\n tag?: never\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.fetch'\n */\n requestTag?: string\n}) => Promise<{\n data: ClientReturn<QueryString>\n sourceMap: ContentSourceMap | null\n tags: string[]\n}>\n\n/**\n * @public\n */\nexport type DefinedSanityLiveStreamType = <const QueryString extends string>(props: {\n query: QueryString\n params?: QueryParams | Promise<QueryParams>\n /**\n * Add custom `next.tags` to the underlying fetch request.\n * @see https://nextjs.org/docs/app/api-reference/functions/fetch#optionsnexttags\n * This can be used in conjunction with custom fallback revalidation strategies, as well as with custom Server Actions that mutate data and want to render with fresh data right away (faster than the Live Event latency).\n * @defaultValue `['sanity']`\n */\n tags?: string[]\n perspective?: Exclude<ClientPerspective, 'raw'>\n stega?: boolean\n /**\n * @deprecated use `requestTag` instead\n */\n tag?: never\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.live-stream.fetch'\n */\n requestTag?: string\n children: (result: {\n data: ClientReturn<QueryString>\n sourceMap: ContentSourceMap | null\n tags: string[]\n }) => Promise<Awaited<React.ReactNode>>\n // @TODO follow up on this after React 19: https://github.com/vercel/next.js/discussions/67365#discussioncomment-9935377\n // }) => Promise<Awaited<React.ReactNode>>\n}) => React.ReactNode\n\n/**\n * @public\n */\nexport interface DefinedSanityLiveProps {\n /**\n * Automatic refresh of RSC when the component <SanityLive /> is mounted.\n * Note that this is different from revalidation, which is based on tags and causes `sanityFetch` calls to be re-fetched.\n * @defaultValue `true`\n */\n refreshOnMount?: boolean\n /**\n * Automatically refresh when window gets focused\n * Note that this is different from revalidation, which is based on tags and causes `sanityFetch` calls to be re-fetched.\n * @defaultValue `false` if draftMode().isEnabled, otherwise `true` if not inside an iframe\n */\n refreshOnFocus?: boolean\n /**\n * Automatically refresh when the browser regains a network connection (via navigator.onLine)\n * Note that this is different from revalidation, which is based on tags and causes `sanityFetch` calls to be re-fetched.\n * @defaultValue `true`\n */\n refreshOnReconnect?: boolean\n /**\n * Automatically refresh on an interval when the Live Event API emits a `goaway` event, which indicates that the connection is rejected or closed.\n * This typically happens if the connection limit is reached, or if the connection is idle for too long.\n * To disable this long polling fallback behavior set `intervalOnGoAway` to `false` or `0`.\n * You can also use `onGoAway` to handle the `goaway` event in your own way, and read the reason why the event was emitted.\n * @defaultValue `30_000` 30 seconds interval\n */\n intervalOnGoAway?: number | false\n\n /**\n * @deprecated use `requestTag` instead\n */\n tag?: never\n\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.live'\n */\n requestTag?: string\n\n /**\n * Handle errors from the Live Events subscription.\n * By default it's reported using `console.error`, you can override this prop to handle it in your own way.\n */\n onError?: (error: unknown) => void\n\n /**\n * Handle the `goaway` event if the connection is rejected/closed.\n * `event.reason` will be a string of why the event was emitted, for example `'connection limit reached'`.\n * When this happens the `<SanityLive />` will fallback to long polling with a default interval of 30 seconds, providing your own `onGoAway` handler does not change this behavior.\n * If you want to disable long polling set `intervalOnGoAway` to `false` or `0`.\n */\n onGoAway?: (event: LiveEventGoAway, intervalOnGoAway: number | false) => void\n\n /**\n * Override how cache tags are invalidated, you need to pass a server action here.\n * You can also pass a `use client` function here, and have `router.refresh()` be called if the promise resolves to `'refresh'`.\n */\n revalidateSyncTags?: (tags: SyncTag[]) => Promise<void | 'refresh'>\n}\n\n/**\n * @public\n */\nexport interface DefineSanityLiveOptions {\n /**\n * Required for `sanityFetch` and `SanityLive` to work\n */\n client: SanityClient\n /**\n * Optional. If provided then the token needs to have permissions to query documents with `drafts.` prefixes in order for `perspective: 'drafts'` to work.\n * This token is not shared with the browser.\n */\n serverToken?: string | false\n /**\n * Optional. This token is shared with the browser, and should only have access to query published documents.\n * It is used to setup a `Live Draft Content` EventSource connection, and enables live previewing drafts stand-alone, outside of Presentation Tool.\n */\n browserToken?: string | false\n /**\n * Fetch options used by `sanityFetch`\n */\n fetchOptions?: {\n /**\n * Optional, enables time based revalidation in addition to the EventSource connection.\n * @defaultValue `false`\n */\n revalidate?: number | false\n }\n /**\n * Optional. Include stega encoding when draft mode is enabled.\n * @defaultValue `true`\n */\n stega?: boolean\n}\n\n// export type VerifyPreviewSecretType = (\n// secret: string,\n// ) => Promise<{isValid: boolean; studioUrl: string | null}>\n\n/**\n * @public\n */\nexport function defineLive(config: DefineSanityLiveOptions): {\n /**\n * Use this function to fetch data from Sanity in your React Server Components.\n * @public\n */\n sanityFetch: DefinedSanityFetchType\n /**\n * Render this in your root layout.tsx to make your page revalidate on new content live, automatically.\n * @public\n */\n SanityLive: React.ComponentType<DefinedSanityLiveProps>\n /**\n * @alpha experimental, it may change or even be removed at any time\n */\n SanityLiveStream: DefinedSanityLiveStreamType\n // verifyPreviewSecret: VerifyPreviewSecretType\n} {\n const {\n client: _client,\n serverToken,\n browserToken,\n fetchOptions,\n stega: stegaEnabled = true,\n } = config\n\n if (!_client) {\n throw new Error('`client` is required for `defineLive` to function')\n }\n\n if (process.env.NODE_ENV !== 'production' && !serverToken && serverToken !== false) {\n console.warn(\n 'No `serverToken` provided to `defineLive`. This means that only published content will be fetched and respond to live events. You can silence this warning by setting `serverToken: false`.',\n )\n }\n\n if (process.env.NODE_ENV !== 'production' && !browserToken && browserToken !== false) {\n console.warn(\n 'No `browserToken` provided to `defineLive`. This means that live previewing drafts will only work when using the Presentation Tool in your Sanity Studio. To support live previewing drafts stand-alone, provide a `browserToken`. It is shared with the browser so it should only have Viewer rights or lower. You can silence this warning by setting `browserToken: false`.',\n )\n }\n\n const client = _client.withConfig({allowReconfigure: false, useCdn: false})\n const {token: originalToken} = client.config()\n const studioUrlDefined = typeof client.config().stega.studioUrl !== 'undefined'\n\n const sanityFetch: DefinedSanityFetchType = async function sanityFetch<\n const QueryString extends string,\n >({\n query,\n params = {},\n stega: _stega,\n tags = ['sanity'],\n perspective: _perspective,\n tag,\n requestTag = tag ?? 'next-loader.fetch',\n }: {\n query: QueryString\n params?: QueryParams | Promise<QueryParams>\n stega?: boolean\n tags?: string[]\n perspective?: Exclude<ClientPerspective, 'raw'>\n tag?: string\n requestTag?: string\n }) {\n const stega = _stega ?? (stegaEnabled && studioUrlDefined && (await draftMode()).isEnabled)\n const perspective = _perspective ?? (await resolveCookiePerspective())\n const useCdn = perspective === 'published'\n const revalidate =\n fetchOptions?.revalidate !== undefined\n ? fetchOptions.revalidate\n : process.env.NODE_ENV === 'production'\n ? false\n : undefined\n\n // fetch the tags first, with revalidate to 1s to ensure we get the latest tags, eventually\n const {syncTags} = await client.fetch(query, await params, {\n filterResponse: false,\n perspective: perspective as ClientPerspective,\n stega: false,\n returnQuery: false,\n next: {revalidate, tags: [...tags, 'sanity:fetch-sync-tags']},\n useCdn,\n cacheMode: useCdn ? 'noStale' : undefined,\n tag: [requestTag, 'fetch-sync-tags'].filter(Boolean).join('.'),\n })\n\n const cacheTags = [...tags, ...(syncTags?.map((tag) => `sanity:${tag}`) || [])]\n\n const {result, resultSourceMap} = await client.fetch(query, await params, {\n filterResponse: false,\n perspective: perspective as ClientPerspective,\n stega,\n token: perspective !== 'published' && serverToken ? serverToken : originalToken,\n next: {revalidate, tags: cacheTags},\n useCdn,\n cacheMode: useCdn ? 'noStale' : undefined,\n tag: requestTag,\n })\n return {data: result, sourceMap: resultSourceMap || null, tags: cacheTags}\n }\n\n const SanityLive: React.ComponentType<DefinedSanityLiveProps> = async function SanityLive(props) {\n const {\n // handleDraftModeAction = handleDraftModeActionMissing\n refreshOnMount,\n refreshOnFocus,\n refreshOnReconnect,\n tag,\n requestTag = tag,\n onError,\n onGoAway,\n intervalOnGoAway,\n revalidateSyncTags,\n } = props\n const {projectId, dataset, apiHost, apiVersion, useProjectHostname, requestTagPrefix} =\n client.config()\n const {isEnabled: isDraftModeEnabled} = await draftMode()\n\n // Preconnect to the Live Event API origin, or at least prefetch the DNS if preconenct is not supported\n const {origin} = new URL(client.getUrl('', false))\n preconnect(origin)\n prefetchDNS(origin)\n\n return (\n <SanityLiveClientComponent\n projectId={projectId}\n dataset={dataset}\n apiHost={apiHost}\n apiVersion={apiVersion}\n useProjectHostname={useProjectHostname}\n requestTagPrefix={requestTagPrefix}\n requestTag={requestTag}\n token={typeof browserToken === 'string' && isDraftModeEnabled ? browserToken : undefined}\n draftModeEnabled={isDraftModeEnabled}\n // handleDraftModeAction={handleDraftModeAction}\n draftModePerspective={await resolveCookiePerspective()}\n refreshOnMount={refreshOnMount}\n refreshOnFocus={refreshOnFocus}\n refreshOnReconnect={refreshOnReconnect}\n onError={onError}\n onGoAway={onGoAway}\n intervalOnGoAway={intervalOnGoAway}\n revalidateSyncTags={revalidateSyncTags}\n />\n )\n }\n\n const SanityLiveStream: DefinedSanityLiveStreamType = async function SanityLiveStream(props) {\n const {\n query,\n params,\n perspective: _perspective,\n stega: _stega,\n tags,\n children,\n tag,\n requestTag = tag ?? 'next-loader.live-stream.fetch',\n } = props\n const {\n data,\n sourceMap,\n tags: cacheTags,\n } = await sanityFetch({\n query,\n params,\n tags,\n perspective: _perspective,\n stega: _stega,\n requestTag,\n })\n const {isEnabled: isDraftModeEnabled} = await draftMode()\n\n if (isDraftModeEnabled) {\n const stega = _stega ?? (stegaEnabled && studioUrlDefined && (await draftMode()).isEnabled)\n const perspective = _perspective ?? (await resolveCookiePerspective())\n const {projectId, dataset} = client.config()\n return (\n <SanityLiveStreamClientComponent\n projectId={projectId}\n dataset={dataset}\n query={query}\n params={await params}\n perspective={perspective}\n stega={stega}\n initial={children({data, sourceMap, tags: cacheTags})}\n // oxlint-disable-next-line react/no-children-prop\n children={\n // oxlint-disable-next-line no-unsafe-type-assertion\n children as unknown as any\n }\n />\n )\n }\n\n return children({data, sourceMap, tags: cacheTags})\n }\n\n // const verifyPreviewSecret: VerifyPreviewSecretType = async (secret) => {\n // if (!serverToken) {\n // throw new Error(\n // '`serverToken` is required to verify a preview secrets and initiate draft mode',\n // )\n // }\n\n // if (typeof secret !== 'string') {\n // throw new TypeError('`secret` must be a string')\n // }\n // if (!secret.trim()) {\n // throw new Error('`secret` must not be an empty string')\n // }\n\n // const client = _client.withConfig({\n // // Use the token that is setup to query draft documents, it should also have permission to query for secrets\n // token: serverToken,\n // // Userland might be using an API version that's too old to use perspectives\n // apiVersion,\n // // We can't use the CDN, the secret is typically validated right after it's created\n // useCdn: false,\n // // Don't waste time returning a source map, we don't need it\n // resultSourceMap: false,\n // // Stega is not needed\n // stega: false,\n // })\n // const {isValid, studioUrl} = await validateSecret(client, secret, false)\n // return {isValid, studioUrl}\n // }\n\n return {\n sanityFetch,\n SanityLive,\n SanityLiveStream,\n // verifyPreviewSecret\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"live.js","names":["sanityFetch: DefinedSanityFetchType","sanityFetch","tag"],"sources":["../src/live/resolveCookiePerspective.ts","../src/live/defineLive.tsx"],"sourcesContent":["import type {ClientPerspective} from '@sanity/client'\n\nimport {perspectiveCookieName} from '@sanity/preview-url-secret/constants'\nimport {cookies, draftMode} from 'next/headers'\n\nimport {sanitizePerspective} from './utils'\n\n/**\n * @internal\n */\nexport async function resolveCookiePerspective(): Promise<Exclude<ClientPerspective, 'raw'>> {\n return (await draftMode()).isEnabled\n ? (await cookies()).has(perspectiveCookieName)\n ? sanitizePerspective((await cookies()).get(perspectiveCookieName)?.value, 'drafts')\n : 'drafts'\n : 'published'\n}\n","import {\n type ClientPerspective,\n type ClientReturn,\n type ContentSourceMap,\n type LiveEventGoAway,\n type QueryParams,\n type SanityClient,\n type SyncTag,\n} from '@sanity/client'\nimport SanityLiveClientComponent from 'next-sanity/live/client-components/live'\nimport SanityLiveStreamClientComponent from 'next-sanity/live/client-components/live-stream'\nimport {draftMode} from 'next/headers'\nimport {prefetchDNS, preconnect} from 'react-dom'\n\nimport {resolveCookiePerspective} from './resolveCookiePerspective'\n\n/**\n * @public\n */\nexport type DefinedSanityFetchType = <const QueryString extends string>(options: {\n query: QueryString\n params?: QueryParams | Promise<QueryParams>\n /**\n * Add custom `next.tags` to the underlying fetch request.\n * @see https://nextjs.org/docs/app/api-reference/functions/fetch#optionsnexttags\n * This can be used in conjunction with custom fallback revalidation strategies, as well as with custom Server Actions that mutate data and want to render with fresh data right away (faster than the Live Event latency).\n * @defaultValue `['sanity']`\n */\n tags?: string[]\n perspective?: Exclude<ClientPerspective, 'raw'>\n stega?: boolean\n /**\n * @deprecated use `requestTag` instead\n */\n tag?: never\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.fetch'\n */\n requestTag?: string\n}) => Promise<{\n data: ClientReturn<QueryString>\n sourceMap: ContentSourceMap | null\n tags: string[]\n}>\n\n/**\n * @public\n */\nexport type DefinedSanityLiveStreamType = <const QueryString extends string>(props: {\n query: QueryString\n params?: QueryParams | Promise<QueryParams>\n /**\n * Add custom `next.tags` to the underlying fetch request.\n * @see https://nextjs.org/docs/app/api-reference/functions/fetch#optionsnexttags\n * This can be used in conjunction with custom fallback revalidation strategies, as well as with custom Server Actions that mutate data and want to render with fresh data right away (faster than the Live Event latency).\n * @defaultValue `['sanity']`\n */\n tags?: string[]\n perspective?: Exclude<ClientPerspective, 'raw'>\n stega?: boolean\n /**\n * @deprecated use `requestTag` instead\n */\n tag?: never\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.live-stream.fetch'\n */\n requestTag?: string\n children: (result: {\n data: ClientReturn<QueryString>\n sourceMap: ContentSourceMap | null\n tags: string[]\n }) => Promise<Awaited<React.ReactNode>>\n // @TODO follow up on this after React 19: https://github.com/vercel/next.js/discussions/67365#discussioncomment-9935377\n // }) => Promise<Awaited<React.ReactNode>>\n}) => React.ReactNode\n\n/**\n * @public\n */\nexport interface DefinedSanityLiveProps {\n /**\n * Automatic refresh of RSC when the component <SanityLive /> is mounted.\n * Note that this is different from revalidation, which is based on tags and causes `sanityFetch` calls to be re-fetched.\n * @defaultValue `true`\n */\n refreshOnMount?: boolean\n /**\n * Automatically refresh when window gets focused\n * Note that this is different from revalidation, which is based on tags and causes `sanityFetch` calls to be re-fetched.\n * @defaultValue `false` if draftMode().isEnabled, otherwise `true` if not inside an iframe\n */\n refreshOnFocus?: boolean\n /**\n * Automatically refresh when the browser regains a network connection (via navigator.onLine)\n * Note that this is different from revalidation, which is based on tags and causes `sanityFetch` calls to be re-fetched.\n * @defaultValue `true`\n */\n refreshOnReconnect?: boolean\n /**\n * Automatically refresh on an interval when the Live Event API emits a `goaway` event, which indicates that the connection is rejected or closed.\n * This typically happens if the connection limit is reached, or if the connection is idle for too long.\n * To disable this long polling fallback behavior set `intervalOnGoAway` to `false` or `0`.\n * You can also use `onGoAway` to handle the `goaway` event in your own way, and read the reason why the event was emitted.\n * @defaultValue `30_000` 30 seconds interval\n */\n intervalOnGoAway?: number | false\n\n /**\n * @deprecated use `requestTag` instead\n */\n tag?: never\n\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.live'\n */\n requestTag?: string\n\n /**\n * Handle errors from the Live Events subscription.\n * By default it's reported using `console.error`, you can override this prop to handle it in your own way.\n */\n onError?: (error: unknown) => void\n\n /**\n * Handle the `goaway` event if the connection is rejected/closed.\n * `event.reason` will be a string of why the event was emitted, for example `'connection limit reached'`.\n * When this happens the `<SanityLive />` will fallback to long polling with a default interval of 30 seconds, providing your own `onGoAway` handler does not change this behavior.\n * If you want to disable long polling set `intervalOnGoAway` to `false` or `0`.\n */\n onGoAway?: (event: LiveEventGoAway, intervalOnGoAway: number | false) => void\n\n /**\n * Override how cache tags are invalidated, you need to pass a server action here.\n * You can also pass a `use client` function here, and have `router.refresh()` be called if the promise resolves to `'refresh'`.\n */\n revalidateSyncTags?: (tags: SyncTag[]) => Promise<void | 'refresh'>\n}\n\n/**\n * @public\n */\nexport interface DefineSanityLiveOptions {\n /**\n * Required for `sanityFetch` and `SanityLive` to work\n */\n client: SanityClient\n /**\n * Optional. If provided then the token needs to have permissions to query documents with `drafts.` prefixes in order for `perspective: 'drafts'` to work.\n * This token is not shared with the browser.\n */\n serverToken?: string | false\n /**\n * Optional. This token is shared with the browser, and should only have access to query published documents.\n * It is used to setup a `Live Draft Content` EventSource connection, and enables live previewing drafts stand-alone, outside of Presentation Tool.\n */\n browserToken?: string | false\n /**\n * Fetch options used by `sanityFetch`\n */\n fetchOptions?: {\n /**\n * Optional, enables time based revalidation in addition to the EventSource connection.\n * @defaultValue `false`\n */\n revalidate?: number | false\n }\n /**\n * Optional. Include stega encoding when draft mode is enabled.\n * @defaultValue `true`\n */\n stega?: boolean\n}\n\n// export type VerifyPreviewSecretType = (\n// secret: string,\n// ) => Promise<{isValid: boolean; studioUrl: string | null}>\n\n/**\n * @public\n */\nexport function defineLive(config: DefineSanityLiveOptions): {\n /**\n * Use this function to fetch data from Sanity in your React Server Components.\n * @public\n */\n sanityFetch: DefinedSanityFetchType\n /**\n * Render this in your root layout.tsx to make your page revalidate on new content live, automatically.\n * @public\n */\n SanityLive: React.ComponentType<DefinedSanityLiveProps>\n /**\n * @alpha experimental, it may change or even be removed at any time\n */\n SanityLiveStream: DefinedSanityLiveStreamType\n // verifyPreviewSecret: VerifyPreviewSecretType\n} {\n const {\n client: _client,\n serverToken,\n browserToken,\n fetchOptions,\n stega: stegaEnabled = true,\n } = config\n\n if (!_client) {\n throw new Error('`client` is required for `defineLive` to function')\n }\n\n if (process.env.NODE_ENV !== 'production' && !serverToken && serverToken !== false) {\n console.warn(\n 'No `serverToken` provided to `defineLive`. This means that only published content will be fetched and respond to live events. You can silence this warning by setting `serverToken: false`.',\n )\n }\n\n if (process.env.NODE_ENV !== 'production' && !browserToken && browserToken !== false) {\n console.warn(\n 'No `browserToken` provided to `defineLive`. This means that live previewing drafts will only work when using the Presentation Tool in your Sanity Studio. To support live previewing drafts stand-alone, provide a `browserToken`. It is shared with the browser so it should only have Viewer rights or lower. You can silence this warning by setting `browserToken: false`.',\n )\n }\n\n const client = _client.withConfig({allowReconfigure: false, useCdn: false})\n const {token: originalToken} = client.config()\n const studioUrlDefined = typeof client.config().stega.studioUrl !== 'undefined'\n\n const sanityFetch: DefinedSanityFetchType = async function sanityFetch<\n const QueryString extends string,\n >({\n query,\n params = {},\n stega: _stega,\n tags = ['sanity'],\n perspective: _perspective,\n tag,\n requestTag = tag ?? 'next-loader.fetch',\n }: {\n query: QueryString\n params?: QueryParams | Promise<QueryParams>\n stega?: boolean\n tags?: string[]\n perspective?: Exclude<ClientPerspective, 'raw'>\n tag?: string\n requestTag?: string\n }) {\n const stega = _stega ?? (stegaEnabled && studioUrlDefined && (await draftMode()).isEnabled)\n const perspective = _perspective ?? (await resolveCookiePerspective())\n const useCdn = perspective === 'published'\n const revalidate =\n fetchOptions?.revalidate !== undefined\n ? fetchOptions.revalidate\n : process.env.NODE_ENV === 'production'\n ? false\n : undefined\n\n // fetch the tags first, with revalidate to 1s to ensure we get the latest tags, eventually\n const {syncTags} = await client.fetch(query, await params, {\n filterResponse: false,\n perspective: perspective as ClientPerspective,\n stega: false,\n returnQuery: false,\n next: {revalidate, tags: [...tags, 'sanity:fetch-sync-tags']},\n useCdn,\n cacheMode: useCdn ? 'noStale' : undefined,\n tag: [requestTag, 'fetch-sync-tags'].filter(Boolean).join('.'),\n })\n\n const cacheTags = [...tags, ...(syncTags?.map((tag) => `sanity:${tag}`) || [])]\n\n const {result, resultSourceMap} = await client.fetch(query, await params, {\n filterResponse: false,\n perspective: perspective as ClientPerspective,\n stega,\n token: perspective !== 'published' && serverToken ? serverToken : originalToken,\n next: {revalidate, tags: cacheTags},\n useCdn,\n cacheMode: useCdn ? 'noStale' : undefined,\n tag: requestTag,\n })\n return {data: result, sourceMap: resultSourceMap || null, tags: cacheTags}\n }\n\n const SanityLive: React.ComponentType<DefinedSanityLiveProps> = async function SanityLive(props) {\n const {\n // handleDraftModeAction = handleDraftModeActionMissing\n refreshOnMount,\n refreshOnFocus,\n refreshOnReconnect,\n tag,\n requestTag = tag,\n onError,\n onGoAway,\n intervalOnGoAway,\n revalidateSyncTags,\n } = props\n const {projectId, dataset, apiHost, apiVersion, useProjectHostname, requestTagPrefix} =\n client.config()\n const {isEnabled: isDraftModeEnabled} = await draftMode()\n\n // Preconnect to the Live Event API origin, or at least prefetch the DNS if preconenct is not supported\n const {origin} = new URL(client.getUrl('', false))\n preconnect(origin)\n prefetchDNS(origin)\n\n return (\n <SanityLiveClientComponent\n projectId={projectId}\n dataset={dataset}\n apiHost={apiHost}\n apiVersion={apiVersion}\n useProjectHostname={useProjectHostname}\n requestTagPrefix={requestTagPrefix}\n requestTag={requestTag}\n token={typeof browserToken === 'string' && isDraftModeEnabled ? browserToken : undefined}\n draftModeEnabled={isDraftModeEnabled}\n // handleDraftModeAction={handleDraftModeAction}\n draftModePerspective={await resolveCookiePerspective()}\n refreshOnMount={refreshOnMount}\n refreshOnFocus={refreshOnFocus}\n refreshOnReconnect={refreshOnReconnect}\n onError={onError}\n onGoAway={onGoAway}\n intervalOnGoAway={intervalOnGoAway}\n revalidateSyncTags={revalidateSyncTags}\n />\n )\n }\n\n const SanityLiveStream: DefinedSanityLiveStreamType = async function SanityLiveStream(props) {\n const {\n query,\n params,\n perspective: _perspective,\n stega: _stega,\n tags,\n children,\n tag,\n requestTag = tag ?? 'next-loader.live-stream.fetch',\n } = props\n const {\n data,\n sourceMap,\n tags: cacheTags,\n } = await sanityFetch({\n query,\n params,\n tags,\n perspective: _perspective,\n stega: _stega,\n requestTag,\n })\n const {isEnabled: isDraftModeEnabled} = await draftMode()\n\n if (isDraftModeEnabled) {\n const stega = _stega ?? (stegaEnabled && studioUrlDefined && (await draftMode()).isEnabled)\n const perspective = _perspective ?? (await resolveCookiePerspective())\n const {projectId, dataset} = client.config()\n return (\n <SanityLiveStreamClientComponent\n projectId={projectId}\n dataset={dataset}\n query={query}\n params={await params}\n perspective={perspective}\n stega={stega}\n initial={children({data, sourceMap, tags: cacheTags})}\n // oxlint-disable-next-line react/no-children-prop\n children={\n // oxlint-disable-next-line no-unsafe-type-assertion\n children as unknown as any\n }\n />\n )\n }\n\n return children({data, sourceMap, tags: cacheTags})\n }\n\n // const verifyPreviewSecret: VerifyPreviewSecretType = async (secret) => {\n // if (!serverToken) {\n // throw new Error(\n // '`serverToken` is required to verify a preview secrets and initiate draft mode',\n // )\n // }\n\n // if (typeof secret !== 'string') {\n // throw new TypeError('`secret` must be a string')\n // }\n // if (!secret.trim()) {\n // throw new Error('`secret` must not be an empty string')\n // }\n\n // const client = _client.withConfig({\n // // Use the token that is setup to query draft documents, it should also have permission to query for secrets\n // token: serverToken,\n // // Userland might be using an API version that's too old to use perspectives\n // apiVersion,\n // // We can't use the CDN, the secret is typically validated right after it's created\n // useCdn: false,\n // // Don't waste time returning a source map, we don't need it\n // resultSourceMap: false,\n // // Stega is not needed\n // stega: false,\n // })\n // const {isValid, studioUrl} = await validateSecret(client, secret, false)\n // return {isValid, studioUrl}\n // }\n\n return {\n sanityFetch,\n SanityLive,\n SanityLiveStream,\n // verifyPreviewSecret\n }\n}\n"],"mappings":"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"}
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":[],"sources":["../src/live/utils.ts"],"sourcesContent":["import {validateApiPerspective, type ClientPerspective} from '@sanity/client'\n\n/** @internal */\nexport function sanitizePerspective(\n _perspective: unknown,\n fallback: 'drafts' | 'published',\n): Exclude<ClientPerspective, 'raw'> {\n const perspective =\n typeof _perspective === 'string' && _perspective.includes(',')\n ? _perspective.split(',')\n : _perspective\n try {\n validateApiPerspective(perspective)\n return perspective === 'raw' ? fallback : perspective\n } catch (err) {\n
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../src/live/utils.ts"],"sourcesContent":["import {validateApiPerspective, type ClientPerspective} from '@sanity/client'\n\n/** @internal */\nexport function sanitizePerspective(\n _perspective: unknown,\n fallback: 'drafts' | 'published',\n): Exclude<ClientPerspective, 'raw'> {\n const perspective =\n typeof _perspective === 'string' && _perspective.includes(',')\n ? _perspective.split(',')\n : _perspective\n try {\n validateApiPerspective(perspective)\n return perspective === 'raw' ? fallback : perspective\n } catch (err) {\n console.warn(`Invalid perspective:`, _perspective, perspective, err)\n return fallback\n }\n}\n"],"mappings":";AAGA,SAAgB,oBACd,cACA,UACmC;CACnC,MAAM,cACJ,OAAO,iBAAiB,YAAY,aAAa,SAAS,IAAI,GAC1D,aAAa,MAAM,IAAI,GACvB;AACN,KAAI;AACF,yBAAuB,YAAY;AACnC,SAAO,gBAAgB,QAAQ,WAAW;UACnC,KAAK;AACZ,UAAQ,KAAK,wBAAwB,cAAc,aAAa,IAAI;AACpE,SAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/visual-editing/VisualEditing.tsx"],"sourcesContent":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/visual-editing/VisualEditing.tsx"],"sourcesContent":[],"mappings":";;AAOA;;iBAAgB,aAAA,QAAqB,uBAAqB,KAAA,CAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["autoBasePath: string | undefined","autoTrailingSlash: boolean | undefined"],"sources":["../../src/visual-editing/VisualEditing.tsx"],"sourcesContent":["import type {VisualEditingProps} from 'next-sanity/visual-editing/client-component'\nimport VisualEditingComponent from 'next-sanity/visual-editing/client-component'\n\n/**\n * @public\n */\nexport function VisualEditing(props: VisualEditingProps): React.ReactElement {\n let autoBasePath: string | undefined\n if (typeof props.basePath !== 'string') {\n try {\n autoBasePath = process.env['__NEXT_ROUTER_BASEPATH']\n if (autoBasePath) {\n //
|
|
1
|
+
{"version":3,"file":"index.js","names":["autoBasePath: string | undefined","autoTrailingSlash: boolean | undefined"],"sources":["../../src/visual-editing/VisualEditing.tsx"],"sourcesContent":["import type {VisualEditingProps} from 'next-sanity/visual-editing/client-component'\n\nimport VisualEditingComponent from 'next-sanity/visual-editing/client-component'\n\n/**\n * @public\n */\nexport function VisualEditing(props: VisualEditingProps): React.ReactElement {\n let autoBasePath: string | undefined\n if (typeof props.basePath !== 'string') {\n try {\n autoBasePath = process.env['__NEXT_ROUTER_BASEPATH']\n if (autoBasePath) {\n // oxlint-disable-next-line no-console\n console.log(\n `Detected next basePath as ${JSON.stringify(autoBasePath)} by reading \"process.env.__NEXT_ROUTER_BASEPATH\". If this is incorrect then you can set it manually with the basePath prop on the <VisualEditing /> component.`,\n )\n }\n } catch (err) {\n console.error('Failed detecting basePath', err)\n }\n }\n let autoTrailingSlash: boolean | undefined\n if (typeof props.trailingSlash !== 'boolean') {\n try {\n autoTrailingSlash = Boolean(process.env['__NEXT_TRAILING_SLASH'])\n if (autoTrailingSlash) {\n // oxlint-disable-next-line no-console\n console.log(\n `Detected next trailingSlash as ${JSON.stringify(autoTrailingSlash)} by reading \"process.env.__NEXT_TRAILING_SLASH\". If this is incorrect then you can set it manually with the trailingSlash prop on the <VisualEditing /> component.`,\n )\n }\n } catch (err) {\n console.error('Failed detecting trailingSlash', err)\n }\n }\n return (\n <VisualEditingComponent\n {...props}\n basePath={props.basePath ?? autoBasePath}\n trailingSlash={props.trailingSlash ?? autoTrailingSlash}\n />\n )\n}\n\nexport type {VisualEditingProps} from 'next-sanity/visual-editing/client-component'\n"],"mappings":";;AAOA,SAAgB,cAAc,OAA+C;CAC3E,IAAIA;AACJ,KAAI,OAAO,MAAM,aAAa,SAC5B,KAAI;AACF,iBAAe,QAAQ,IAAI;AAC3B,MAAI,aAEF,SAAQ,IACN,6BAA6B,KAAK,UAAU,aAAa,CAAC,gKAC3D;UAEI,KAAK;AACZ,UAAQ,MAAM,6BAA6B,IAAI;;CAGnD,IAAIC;AACJ,KAAI,OAAO,MAAM,kBAAkB,UACjC,KAAI;AACF,sBAAoB,QAAQ,QAAQ,IAAI,yBAAyB;AACjE,MAAI,kBAEF,SAAQ,IACN,kCAAkC,KAAK,UAAU,kBAAkB,CAAC,oKACrE;UAEI,KAAK;AACZ,UAAQ,MAAM,kCAAkC,IAAI;;AAGxD,QACE,oBAAC,wBAAA;EACC,GAAI;EACJ,UAAU,MAAM,YAAY;EAC5B,eAAe,MAAM,iBAAiB;GACtC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/visual-editing/server-actions/index.ts"],"sourcesContent":["'use server'\nimport {revalidatePath} from 'next/cache'\nimport {draftMode} from 'next/headers'\n\nexport async function revalidateRootLayout(): Promise<void> {\n if (!(await draftMode()).isEnabled) {\n
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/visual-editing/server-actions/index.ts"],"sourcesContent":["'use server'\nimport {revalidatePath} from 'next/cache'\nimport {draftMode} from 'next/headers'\n\nexport async function revalidateRootLayout(): Promise<void> {\n if (!(await draftMode()).isEnabled) {\n console.warn('Skipped revalidatePath request because draft mode is not enabled')\n return\n }\n revalidatePath('/', 'layout')\n}\n"],"mappings":";;;AAIA,eAAsB,uBAAsC;AAC1D,KAAI,EAAE,MAAM,WAAW,EAAE,WAAW;AAClC,UAAQ,KAAK,mEAAmE;AAChF;;AAEF,gBAAe,KAAK,SAAS"}
|
package/dist/webhook/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { SanityDocument } from "@sanity/types";
|
|
2
1
|
import { NextRequest } from "next/server";
|
|
3
2
|
/** @public */
|
|
4
3
|
type ParsedBody<T> = {
|
|
@@ -8,6 +7,15 @@ type ParsedBody<T> = {
|
|
|
8
7
|
isValidSignature: boolean | null;
|
|
9
8
|
body: T | null;
|
|
10
9
|
};
|
|
10
|
+
/** @public */
|
|
11
|
+
interface SanityDocument {
|
|
12
|
+
_id: string;
|
|
13
|
+
_type: string;
|
|
14
|
+
_createdAt: string;
|
|
15
|
+
_updatedAt: string;
|
|
16
|
+
_rev: string;
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
}
|
|
11
19
|
/**
|
|
12
20
|
* Handles parsing the body JSON, and validating its signature. Also waits for Content Lake eventual consistency so you can run your queries
|
|
13
21
|
* without worrying about getting stale data.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/webhook/index.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/webhook/index.ts"],"sourcesContent":[],"mappings":";;AAKY,KAAA,UAAA,CAAA,CAAA,CAAA,GAAA;;AAuBZ;;EACO,gBAAA,EAAA,OAAA,GAAA,IAAA;EAGe,IAAA,EAtBd,CAsBc,GAAA,IAAA;CAAX;;UAlBD,cAAA,CAkBP;;;;;;;;;;;;;iBAJmB,iBAAiB,qBAChC,gFAGJ,QAAQ,WAAW"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../node_modules/.pnpm/@sanity+webhook@4.0.4/node_modules/@sanity/webhook/dist/index.mjs","../../src/webhook/index.ts"],"sourcesContent":["class WebhookSignatureValueError extends Error {\n type = \"WebhookSignatureValueError\";\n statusCode = 401;\n}\nclass WebhookSignatureFormatError extends Error {\n type = \"WebhookSignatureFormatError\";\n statusCode = 400;\n}\nfunction isSignatureError(error) {\n return typeof error == \"object\" && error !== null && \"type\" in error && [\"WebhookSignatureValueError\", \"WebhookSignatureFormatError\"].includes(\n error.type\n );\n}\nconst MINIMUM_TIMESTAMP = 16094592e5, SIGNATURE_HEADER_REGEX = /^t=(\\d+)[, ]+v1=([^, ]+)$/, SIGNATURE_HEADER_NAME = \"sanity-webhook-signature\";\nasync function assertValidSignature(stringifiedPayload, signature, secret) {\n const { timestamp } = decodeSignatureHeader(signature), encoded = await encodeSignatureHeader(stringifiedPayload, timestamp, secret);\n if (signature !== encoded)\n throw new WebhookSignatureValueError(\"Signature is invalid\");\n}\nasync function isValidSignature(stringifiedPayload, signature, secret) {\n try {\n return await assertValidSignature(stringifiedPayload, signature, secret), !0;\n } catch (err) {\n if (isSignatureError(err))\n return !1;\n throw err;\n }\n}\nasync function assertValidRequest(request, secret) {\n const signature = request.headers[SIGNATURE_HEADER_NAME];\n if (Array.isArray(signature))\n throw new WebhookSignatureFormatError(\"Multiple signature headers received\");\n if (typeof signature != \"string\")\n throw new WebhookSignatureValueError(\"Request contained no signature header\");\n if (typeof request.body > \"u\")\n throw new WebhookSignatureFormatError(\"Request contained no parsed request body\");\n if (typeof request.body == \"string\" || Buffer.isBuffer(request.body))\n await assertValidSignature(request.body.toString(\"utf8\"), signature, secret);\n else\n throw new Error(\n \"[@sanity/webhook] `request.body` was not a string/buffer - this can lead to invalid signatures. See the [migration docs](https://github.com/sanity-io/webhook-toolkit#from-parsed-to-unparsed-body) for details on how to fix this.\"\n );\n}\nasync function isValidRequest(request, secret) {\n try {\n return await assertValidRequest(request, secret), !0;\n } catch (err) {\n if (isSignatureError(err))\n return !1;\n throw err;\n }\n}\nasync function encodeSignatureHeader(stringifiedPayload, timestamp, secret) {\n const signature = await createHS256Signature(stringifiedPayload, timestamp, secret);\n return `t=${timestamp},v1=${signature}`;\n}\nfunction decodeSignatureHeader(signaturePayload) {\n if (!signaturePayload)\n throw new WebhookSignatureFormatError(\"Missing or empty signature header\");\n const [, timestamp, hashedPayload] = signaturePayload.trim().match(SIGNATURE_HEADER_REGEX) || [];\n if (!timestamp || !hashedPayload)\n throw new WebhookSignatureFormatError(\"Invalid signature payload format\");\n return {\n timestamp: parseInt(timestamp, 10),\n hashedPayload\n };\n}\nasync function createHS256Signature(stringifiedPayload, timestamp, secret) {\n if (typeof crypto > \"u\")\n throw new TypeError(\n \"The Web Crypto API is not available in this environment, either polyfill `globalThis.crypto` or downgrade to `@sanity/webhook@3` which uses the Node.js `crypto` module.\"\n );\n if (!secret || typeof secret != \"string\")\n throw new WebhookSignatureFormatError(\"Invalid secret provided\");\n if (!stringifiedPayload)\n throw new WebhookSignatureFormatError(\"Can not create signature for empty payload\");\n if (typeof stringifiedPayload != \"string\")\n throw new WebhookSignatureFormatError(\"Payload must be a JSON-encoded string\");\n if (typeof timestamp != \"number\" || isNaN(timestamp) || timestamp < MINIMUM_TIMESTAMP)\n throw new WebhookSignatureFormatError(\n \"Invalid signature timestamp, must be a unix timestamp with millisecond precision\"\n );\n const enc = new TextEncoder(), key = await crypto.subtle.importKey(\n \"raw\",\n enc.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n !1,\n [\"sign\"]\n ), signaturePayload = `${timestamp}.${stringifiedPayload}`, signature = await crypto.subtle.sign(\"HMAC\", key, enc.encode(signaturePayload)), signatureArray = Array.from(new Uint8Array(signature));\n return btoa(String.fromCharCode.apply(null, signatureArray)).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\nfunction requireSignedRequest(options) {\n const parseBody = typeof options.parseBody > \"u\" ? !0 : options.parseBody, respondOnError = typeof options.respondOnError > \"u\" ? !0 : options.respondOnError;\n return async function(request, response, next) {\n try {\n await assertValidRequest(request, options.secret), parseBody && typeof request.body == \"string\" && (request.body = JSON.parse(request.body)), next();\n } catch (err) {\n if (!respondOnError || !isSignatureError(err)) {\n next(err);\n return;\n }\n response.status(err.statusCode).json({ message: err.message });\n }\n };\n}\nexport {\n SIGNATURE_HEADER_NAME,\n WebhookSignatureFormatError,\n WebhookSignatureValueError,\n assertValidRequest,\n assertValidSignature,\n decodeSignatureHeader,\n encodeSignatureHeader,\n isSignatureError,\n isValidRequest,\n isValidSignature,\n requireSignedRequest\n};\n//# sourceMappingURL=index.mjs.map\n","import type {
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../node_modules/.pnpm/@sanity+webhook@4.0.4/node_modules/@sanity/webhook/dist/index.mjs","../../src/webhook/index.ts"],"sourcesContent":["class WebhookSignatureValueError extends Error {\n type = \"WebhookSignatureValueError\";\n statusCode = 401;\n}\nclass WebhookSignatureFormatError extends Error {\n type = \"WebhookSignatureFormatError\";\n statusCode = 400;\n}\nfunction isSignatureError(error) {\n return typeof error == \"object\" && error !== null && \"type\" in error && [\"WebhookSignatureValueError\", \"WebhookSignatureFormatError\"].includes(\n error.type\n );\n}\nconst MINIMUM_TIMESTAMP = 16094592e5, SIGNATURE_HEADER_REGEX = /^t=(\\d+)[, ]+v1=([^, ]+)$/, SIGNATURE_HEADER_NAME = \"sanity-webhook-signature\";\nasync function assertValidSignature(stringifiedPayload, signature, secret) {\n const { timestamp } = decodeSignatureHeader(signature), encoded = await encodeSignatureHeader(stringifiedPayload, timestamp, secret);\n if (signature !== encoded)\n throw new WebhookSignatureValueError(\"Signature is invalid\");\n}\nasync function isValidSignature(stringifiedPayload, signature, secret) {\n try {\n return await assertValidSignature(stringifiedPayload, signature, secret), !0;\n } catch (err) {\n if (isSignatureError(err))\n return !1;\n throw err;\n }\n}\nasync function assertValidRequest(request, secret) {\n const signature = request.headers[SIGNATURE_HEADER_NAME];\n if (Array.isArray(signature))\n throw new WebhookSignatureFormatError(\"Multiple signature headers received\");\n if (typeof signature != \"string\")\n throw new WebhookSignatureValueError(\"Request contained no signature header\");\n if (typeof request.body > \"u\")\n throw new WebhookSignatureFormatError(\"Request contained no parsed request body\");\n if (typeof request.body == \"string\" || Buffer.isBuffer(request.body))\n await assertValidSignature(request.body.toString(\"utf8\"), signature, secret);\n else\n throw new Error(\n \"[@sanity/webhook] `request.body` was not a string/buffer - this can lead to invalid signatures. See the [migration docs](https://github.com/sanity-io/webhook-toolkit#from-parsed-to-unparsed-body) for details on how to fix this.\"\n );\n}\nasync function isValidRequest(request, secret) {\n try {\n return await assertValidRequest(request, secret), !0;\n } catch (err) {\n if (isSignatureError(err))\n return !1;\n throw err;\n }\n}\nasync function encodeSignatureHeader(stringifiedPayload, timestamp, secret) {\n const signature = await createHS256Signature(stringifiedPayload, timestamp, secret);\n return `t=${timestamp},v1=${signature}`;\n}\nfunction decodeSignatureHeader(signaturePayload) {\n if (!signaturePayload)\n throw new WebhookSignatureFormatError(\"Missing or empty signature header\");\n const [, timestamp, hashedPayload] = signaturePayload.trim().match(SIGNATURE_HEADER_REGEX) || [];\n if (!timestamp || !hashedPayload)\n throw new WebhookSignatureFormatError(\"Invalid signature payload format\");\n return {\n timestamp: parseInt(timestamp, 10),\n hashedPayload\n };\n}\nasync function createHS256Signature(stringifiedPayload, timestamp, secret) {\n if (typeof crypto > \"u\")\n throw new TypeError(\n \"The Web Crypto API is not available in this environment, either polyfill `globalThis.crypto` or downgrade to `@sanity/webhook@3` which uses the Node.js `crypto` module.\"\n );\n if (!secret || typeof secret != \"string\")\n throw new WebhookSignatureFormatError(\"Invalid secret provided\");\n if (!stringifiedPayload)\n throw new WebhookSignatureFormatError(\"Can not create signature for empty payload\");\n if (typeof stringifiedPayload != \"string\")\n throw new WebhookSignatureFormatError(\"Payload must be a JSON-encoded string\");\n if (typeof timestamp != \"number\" || isNaN(timestamp) || timestamp < MINIMUM_TIMESTAMP)\n throw new WebhookSignatureFormatError(\n \"Invalid signature timestamp, must be a unix timestamp with millisecond precision\"\n );\n const enc = new TextEncoder(), key = await crypto.subtle.importKey(\n \"raw\",\n enc.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n !1,\n [\"sign\"]\n ), signaturePayload = `${timestamp}.${stringifiedPayload}`, signature = await crypto.subtle.sign(\"HMAC\", key, enc.encode(signaturePayload)), signatureArray = Array.from(new Uint8Array(signature));\n return btoa(String.fromCharCode.apply(null, signatureArray)).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\nfunction requireSignedRequest(options) {\n const parseBody = typeof options.parseBody > \"u\" ? !0 : options.parseBody, respondOnError = typeof options.respondOnError > \"u\" ? !0 : options.respondOnError;\n return async function(request, response, next) {\n try {\n await assertValidRequest(request, options.secret), parseBody && typeof request.body == \"string\" && (request.body = JSON.parse(request.body)), next();\n } catch (err) {\n if (!respondOnError || !isSignatureError(err)) {\n next(err);\n return;\n }\n response.status(err.statusCode).json({ message: err.message });\n }\n };\n}\nexport {\n SIGNATURE_HEADER_NAME,\n WebhookSignatureFormatError,\n WebhookSignatureValueError,\n assertValidRequest,\n assertValidSignature,\n decodeSignatureHeader,\n encodeSignatureHeader,\n isSignatureError,\n isValidRequest,\n isValidSignature,\n requireSignedRequest\n};\n//# sourceMappingURL=index.mjs.map\n","import type {NextRequest} from 'next/server'\n\nimport {isValidSignature, SIGNATURE_HEADER_NAME} from '@sanity/webhook'\n\n/** @public */\nexport type ParsedBody<T> = {\n /**\n * If a secret is given then it returns a boolean. If no secret is provided then no validation is done on the signature, and it'll return `null`\n */\n isValidSignature: boolean | null\n body: T | null\n}\n\n/** @public */\ninterface SanityDocument {\n _id: string\n _type: string\n _createdAt: string\n _updatedAt: string\n _rev: string\n [key: string]: unknown\n}\n\n/**\n * Handles parsing the body JSON, and validating its signature. Also waits for Content Lake eventual consistency so you can run your queries\n * without worrying about getting stale data.\n * @public\n */\nexport async function parseBody<Body = SanityDocument>(\n req: NextRequest,\n secret?: string,\n waitForContentLakeEventualConsistency = true,\n): Promise<ParsedBody<Body>> {\n const signature = req.headers.get(SIGNATURE_HEADER_NAME)\n if (!signature) {\n console.error('Missing signature header')\n return {body: null, isValidSignature: null}\n }\n\n const body = await req.text()\n const validSignature = secret ? await isValidSignature(body, signature, secret.trim()) : null\n\n if (validSignature !== false && waitForContentLakeEventualConsistency) {\n await new Promise((resolve) => setTimeout(resolve, 3000))\n }\n\n return {\n body: body.trim() ? JSON.parse(body) : null,\n isValidSignature: validSignature,\n }\n}\n"],"x_google_ignoreList":[0],"mappings":"AAAA,IAAM,6BAAN,cAAyC,MAAM;CAC7C,OAAO;CACP,aAAa;;AAEf,IAAM,8BAAN,cAA0C,MAAM;CAC9C,OAAO;CACP,aAAa;;AAEf,SAAS,iBAAiB,OAAO;AAC/B,QAAO,OAAO,SAAS,YAAY,UAAU,QAAQ,UAAU,SAAS,CAAC,8BAA8B,8BAA8B,CAAC,SACpI,MAAM,KACP;;AAEH,MAAM,oBAAoB,YAAY,yBAAyB,6BAA6B,wBAAwB;AACpH,eAAe,qBAAqB,oBAAoB,WAAW,QAAQ;CACzE,MAAM,EAAE,cAAc,sBAAsB,UAAU;AACtD,KAAI,cAD8D,MAAM,sBAAsB,oBAAoB,WAAW,OAAO,CAElI,OAAM,IAAI,2BAA2B,uBAAuB;;AAEhE,eAAe,iBAAiB,oBAAoB,WAAW,QAAQ;AACrE,KAAI;AACF,SAAO,MAAM,qBAAqB,oBAAoB,WAAW,OAAO,EAAE,CAAC;UACpE,KAAK;AACZ,MAAI,iBAAiB,IAAI,CACvB,QAAO,CAAC;AACV,QAAM;;;AA2BV,eAAe,sBAAsB,oBAAoB,WAAW,QAAQ;AAE1E,QAAO,KAAK,UAAU,MADJ,MAAM,qBAAqB,oBAAoB,WAAW,OAAO;;AAGrF,SAAS,sBAAsB,kBAAkB;AAC/C,KAAI,CAAC,iBACH,OAAM,IAAI,4BAA4B,oCAAoC;CAC5E,MAAM,GAAG,WAAW,iBAAiB,iBAAiB,MAAM,CAAC,MAAM,uBAAuB,IAAI,EAAE;AAChG,KAAI,CAAC,aAAa,CAAC,cACjB,OAAM,IAAI,4BAA4B,mCAAmC;AAC3E,QAAO;EACL,WAAW,SAAS,WAAW,GAAG;EAClC;EACD;;AAEH,eAAe,qBAAqB,oBAAoB,WAAW,QAAQ;AACzE,KAAI,OAAO,SAAS,IAClB,OAAM,IAAI,UACR,2KACD;AACH,KAAI,CAAC,UAAU,OAAO,UAAU,SAC9B,OAAM,IAAI,4BAA4B,0BAA0B;AAClE,KAAI,CAAC,mBACH,OAAM,IAAI,4BAA4B,6CAA6C;AACrF,KAAI,OAAO,sBAAsB,SAC/B,OAAM,IAAI,4BAA4B,wCAAwC;AAChF,KAAI,OAAO,aAAa,YAAY,MAAM,UAAU,IAAI,YAAY,kBAClE,OAAM,IAAI,4BACR,mFACD;CACH,MAAM,MAAM,IAAI,aAAa,EAAE,MAAM,MAAM,OAAO,OAAO,UACvD,OACA,IAAI,OAAO,OAAO,EAClB;EAAE,MAAM;EAAQ,MAAM;EAAW,EACjC,CAAC,GACD,CAAC,OAAO,CACT,EAAE,mBAAmB,GAAG,UAAU,GAAG,sBAAsB,YAAY,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,IAAI,OAAO,iBAAiB,CAAC,EAAE,iBAAiB,MAAM,KAAK,IAAI,WAAW,UAAU,CAAC;AACnM,QAAO,KAAK,OAAO,aAAa,MAAM,MAAM,eAAe,CAAC,CAAC,QAAQ,OAAO,IAAI,CAAC,QAAQ,OAAO,IAAI,CAAC,QAAQ,OAAO,GAAG;;AC7DzH,eAAsB,UACpB,KACA,QACA,wCAAwC,MACb;CAC3B,MAAM,YAAY,IAAI,QAAQ,IAAI,sBAAsB;AACxD,KAAI,CAAC,WAAW;AACd,UAAQ,MAAM,2BAA2B;AACzC,SAAO;GAAC,MAAM;GAAM,kBAAkB;GAAK;;CAG7C,MAAM,OAAO,MAAM,IAAI,MAAM;CAC7B,MAAM,iBAAiB,SAAS,MAAM,iBAAiB,MAAM,WAAW,OAAO,MAAM,CAAC,GAAG;AAEzF,KAAI,mBAAmB,SAAS,sCAC9B,OAAM,IAAI,SAAS,YAAY,WAAW,SAAS,IAAK,CAAC;AAG3D,QAAO;EACL,MAAM,KAAK,MAAM,GAAG,KAAK,MAAM,KAAK,GAAG;EACvC,kBAAkB;EACnB"}
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-sanity",
|
|
3
|
-
"version": "12.0.
|
|
3
|
+
"version": "12.0.4",
|
|
4
4
|
"description": "Sanity.io toolkit for Next.js",
|
|
5
5
|
"keywords": [
|
|
6
|
+
"live",
|
|
7
|
+
"next.js",
|
|
8
|
+
"preview",
|
|
6
9
|
"sanity",
|
|
7
10
|
"sanity.io",
|
|
8
|
-
"next.js",
|
|
9
11
|
"studio",
|
|
10
|
-
"studio-v3"
|
|
11
|
-
"live",
|
|
12
|
-
"preview"
|
|
12
|
+
"studio-v3"
|
|
13
13
|
],
|
|
14
14
|
"homepage": "https://github.com/sanity-io/next-sanity#readme",
|
|
15
15
|
"bugs": {
|
|
@@ -20,10 +20,11 @@
|
|
|
20
20
|
"url": "git+ssh://git@github.com/sanity-io/next-sanity.git",
|
|
21
21
|
"directory": "packages/next-sanity"
|
|
22
22
|
},
|
|
23
|
-
"license": "MIT",
|
|
24
23
|
"author": "Sanity.io <hello@sanity.io>",
|
|
24
|
+
"license": "MIT",
|
|
25
25
|
"sideEffects": false,
|
|
26
26
|
"type": "module",
|
|
27
|
+
"main": "./dist/index.js",
|
|
27
28
|
"exports": {
|
|
28
29
|
".": "./dist/index.js",
|
|
29
30
|
"./draft-mode": "./dist/draft-mode/index.js",
|
|
@@ -46,13 +47,11 @@
|
|
|
46
47
|
"./webhook": "./dist/webhook/index.js",
|
|
47
48
|
"./package.json": "./package.json"
|
|
48
49
|
},
|
|
49
|
-
"main": "./dist/index.js",
|
|
50
50
|
"module": "./dist/index.js",
|
|
51
51
|
"types": "./dist/index.d.ts",
|
|
52
52
|
"files": [
|
|
53
53
|
"dist"
|
|
54
54
|
],
|
|
55
|
-
"browserslist": "extends @sanity/browserslist-config",
|
|
56
55
|
"dependencies": {
|
|
57
56
|
"@portabletext/react": "^6.0.0",
|
|
58
57
|
"@sanity/client": "^7.13.2",
|
|
@@ -67,20 +66,18 @@
|
|
|
67
66
|
},
|
|
68
67
|
"devDependencies": {
|
|
69
68
|
"@sanity/browserslist-config": "^1.0.5",
|
|
70
|
-
"@sanity/pkg-utils": "^10.2.1",
|
|
71
69
|
"@sanity/tsconfig": "^2.1.0",
|
|
72
|
-
"@sanity/types": "^5.0.1",
|
|
73
70
|
"@sanity/webhook": "4.0.4",
|
|
74
71
|
"@types/node": "^24.10.4",
|
|
75
72
|
"@types/react": "^19.2.7",
|
|
76
73
|
"@types/react-dom": "^19.2.3",
|
|
77
74
|
"@vitest/coverage-v8": "^4.0.16",
|
|
78
|
-
"next": "16.1.0-canary.
|
|
75
|
+
"next": "16.1.0-canary.34",
|
|
79
76
|
"publint": "^0.3.16",
|
|
80
77
|
"react": "^19.2.3",
|
|
81
78
|
"react-dom": "^19.2.3",
|
|
82
79
|
"styled-components": "^6.1.19",
|
|
83
|
-
"tsdown": "0.18.
|
|
80
|
+
"tsdown": "0.18.1",
|
|
84
81
|
"typescript": "5.9.3",
|
|
85
82
|
"vitest": "^4.0.16"
|
|
86
83
|
},
|
|
@@ -92,6 +89,7 @@
|
|
|
92
89
|
"sanity": "^5.0.1",
|
|
93
90
|
"styled-components": "^6.1"
|
|
94
91
|
},
|
|
92
|
+
"browserslist": "extends @sanity/browserslist-config",
|
|
95
93
|
"engines": {
|
|
96
94
|
"node": ">=20.19 <22 || >=22.12"
|
|
97
95
|
},
|