@sanity/preview-kit
Version:
General purpose utils for live content and visual editing
1 lines • 21.4 kB
Source Map (JSON)
{"version":3,"file":"LiveQueryProvider.cjs","sources":["../../src/LiveQueryProvider/constants.ts","../../src/LiveQueryProvider/useLiveEvents.ts","../../src/LiveQueryProvider/utils.ts","../../src/LiveQueryProvider/useLiveQueries.ts","../../src/LiveQueryProvider/usePerspective.ts","../../src/LiveQueryProvider/LiveQueryProvider.tsx"],"sourcesContent":["export const DEFAULT_TAG = 'sanity.preview-kit'\n","import {type LiveEvent, type LiveEventMessage} from '@sanity/client'\nimport {useDeferredValue, useEffect, useReducer, useState} from 'react'\n\nimport type {LiveQueryProviderProps} from '../types'\nimport {DEFAULT_TAG} from './constants'\n\ntype State = {\n /**\n * Growing list over live events with Sync Tags,\n * that can be used to refetch with Sanity Client, using the id as the lastLiveEventId parameter\n */\n messages: LiveEventMessage[]\n /**\n * If the connection experiences a reconnect, or a restart event is received, the counter is incremented.\n * This counter is suitable as a `key` on React Components as a way to reset its internal state and refetch.\n */\n resets: number\n}\n\nexport function reducer(state: State, event: LiveEvent): State {\n switch (event.type) {\n case 'message':\n return {\n ...state,\n messages: [...state.messages, event],\n }\n case 'reconnect':\n case 'restart':\n return {\n ...state,\n messages: [],\n resets: state.resets + 1,\n }\n case 'welcome':\n // no-op\n return state\n default:\n throw Error(\n `Unknown event: ${\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (event as any).type\n }`,\n {cause: event},\n )\n }\n}\n\nexport const initialState: State = {\n messages: [],\n resets: 0,\n}\n\nexport function useLiveEvents(client: LiveQueryProviderProps['client']): State {\n const [state, dispatch] = useReducer(reducer, initialState)\n const [error, setError] = useState<unknown>(null)\n if (error !== null) {\n // Push error to nearest error boundary\n throw error\n }\n\n useEffect(() => {\n const subscription = client.live.events({includeDrafts: true, tag: DEFAULT_TAG}).subscribe({\n next: dispatch,\n error: (err) =>\n setError(\n err instanceof Error ? err : new Error('Unexpected error in useLiveEvents', {cause: err}),\n ),\n })\n return () => subscription.unsubscribe()\n }, [client.live])\n\n return useDeferredValue(state)\n}\n","import {type ClientPerspective, type QueryParams} from '@sanity/client'\n\n/**\n * Cache key format: `query:{\"params\":...,\"perspective\":...}`\n * @internal\n */\nexport type QueryCacheKey = `${string}:${string}`\n\nexport function getQueryCacheKey(\n query: string,\n params: QueryParams,\n perspective: Exclude<ClientPerspective, 'raw'>,\n): QueryCacheKey {\n return `${query}:${JSON.stringify({params, perspective})}`\n}\n","import {\n type ClientPerspective,\n type ContentSourceMap,\n type QueryParams,\n type SyncTag,\n} from '@sanity/client'\nimport {useCallback, useReducer, useState} from 'react'\nimport isEqual from 'react-fast-compare'\n\nimport {getQueryCacheKey, type QueryCacheKey} from './utils'\n\nexport type OnStoreChange = () => void\n\ntype LiveQueriesState = Map<\n QueryCacheKey,\n {\n query: string\n params: QueryParams\n perspective: Exclude<ClientPerspective, 'raw'>\n listeners: Set<OnStoreChange>\n }\n>\n\nexport type LiveSnapshots = Map<\n QueryCacheKey,\n {\n result: unknown\n resultSourceMap: ContentSourceMap | null | undefined\n syncTags: SyncTag[] | undefined\n }\n>\n\ntype SubscribeAction = {\n type: 'subscribe'\n payload: {\n query: string\n params: QueryParams\n perspective: Exclude<ClientPerspective, 'raw'>\n onStoreChange: OnStoreChange\n }\n}\ntype UnsubscribeAction = {\n type: 'unsubscribe'\n payload: {\n query: string\n params: QueryParams\n perspective: Exclude<ClientPerspective, 'raw'>\n onStoreChange: OnStoreChange\n }\n}\nexport type SnapshotAction = {\n type: 'snapshot'\n payload: {\n query: string\n params: QueryParams\n result: unknown\n resultSourceMap: ContentSourceMap | undefined\n perspective: Exclude<ClientPerspective, 'raw'>\n tags: `s1:${string}`[] | undefined\n }\n}\ntype Action = SubscribeAction | UnsubscribeAction | SnapshotAction\n\nfunction subscribe(queries: LiveQueriesState, {payload}: SubscribeAction): LiveQueriesState {\n const key = getQueryCacheKey(payload.query, payload.params, payload.perspective)\n\n if (!queries.get(key)?.listeners.has(payload.onStoreChange)) {\n const nextQueries = new Map(queries)\n const value = nextQueries.get(key) || {\n query: payload.query,\n params: payload.params,\n perspective: payload.perspective,\n listeners: new Set(),\n }\n const listeners = new Set(value.listeners)\n listeners.add(payload.onStoreChange)\n nextQueries.set(key, {...value, listeners})\n return nextQueries\n }\n\n return queries\n}\n\nfunction unsubscribe(queries: LiveQueriesState, {payload}: UnsubscribeAction): LiveQueriesState {\n const key = getQueryCacheKey(payload.query, payload.params, payload.perspective)\n\n const value = queries.get(key)\n if (!value) {\n return queries\n }\n if (!value.listeners.has(payload.onStoreChange)) {\n return queries\n }\n const nextQueries = new Map(queries)\n const listeners = new Set(value.listeners)\n listeners.delete(payload.onStoreChange)\n if (listeners.size === 0) {\n nextQueries.delete(key)\n } else {\n nextQueries.set(key, {...value, listeners})\n }\n return nextQueries\n}\n\nexport function reducer(state: LiveQueriesState, action: Action): LiveQueriesState {\n switch (action.type) {\n case 'subscribe':\n return subscribe(state, action)\n case 'unsubscribe':\n return unsubscribe(state, action)\n default:\n throw Error(\n `Unknown action: ${\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (action as any).type\n }`,\n {cause: action},\n )\n }\n}\n\nexport const initialQueries: LiveQueriesState = new Map()\n\nexport type LiveQueriesDispatch = React.Dispatch<Action>\n\nexport type LiveQueriesUpdate = (\n key: QueryCacheKey,\n result: unknown,\n resultSourceMap: ContentSourceMap | null | undefined,\n syncTags: SyncTag[] | undefined,\n) => boolean\n\n// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport function useLiveQueries() {\n const [queries, dispatch] = useReducer(reducer, initialQueries)\n const [snapshots] = useState<LiveSnapshots>(() => new Map())\n\n const subscribe = useCallback((payload: SubscribeAction['payload']): (() => void) => {\n dispatch({type: 'subscribe', payload})\n return () => dispatch({type: 'unsubscribe', payload})\n }, [])\n /**\n * This handler is intentionally mutating the snapshots state, this is so that useSyncExternalStore hooks\n * can read the current state in its getSnapshot handlers.\n * The caller is responsible for looping over listeners in order to notify the stores.\n */\n const update = useCallback<LiveQueriesUpdate>(\n (key, result, resultSourceMap, syncTags) => {\n const prev = snapshots.get(key)\n if (prev && isEqual(prev, {result, resultSourceMap, syncTags})) {\n return false\n }\n snapshots.set(key, {\n result: isEqual(prev?.result, result) ? prev?.result : result,\n resultSourceMap: isEqual(prev?.resultSourceMap, resultSourceMap)\n ? prev?.resultSourceMap\n : resultSourceMap,\n syncTags: isEqual(prev?.syncTags, syncTags) ? prev?.syncTags : syncTags,\n })\n\n return true\n },\n [snapshots],\n )\n\n return {queries, snapshots, subscribe, update}\n}\n","import type {ClientPerspective} from '@sanity/client'\nimport {createNode, createNodeMachine} from '@sanity/comlink'\nimport {\n createCompatibilityActors,\n type LoaderControllerMsg,\n type LoaderNodeMsg,\n} from '@sanity/presentation-comlink'\nimport {useEffect, useState} from 'react'\nimport isEqual from 'react-fast-compare'\n\nexport function usePerspective(\n initialPerspective: Exclude<ClientPerspective, 'raw'>,\n): Exclude<ClientPerspective, 'raw'> {\n const [presentationPerspective, setPresentationPerspective] = useState<Exclude<\n ClientPerspective,\n 'raw'\n > | null>(null)\n\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.on('loader/perspective', ({perspective}) => {\n if (perspective !== 'raw') {\n setPresentationPerspective((prev) => (isEqual(prev, perspective) ? prev : perspective))\n }\n })\n\n const stop = comlink.start()\n return () => stop()\n }, [])\n return presentationPerspective === null ? initialPerspective : presentationPerspective\n}\n","import type {ClientPerspective, LiveEventMessage, QueryParams, SyncTag} from '@sanity/client'\nimport {useEffect, useMemo, useState} from 'react'\n\nimport {defineStoreContext as Context} from '../context'\nimport {useQueryPerspective, useShouldPause} from '../hooks'\nimport type {\n DefineListenerContext,\n ListenerGetSnapshot,\n ListenerSubscribe,\n LiveQueryProviderProps,\n} from '../types'\nimport {DEFAULT_TAG} from './constants'\nimport {useLiveEvents} from './useLiveEvents'\nimport {type LiveQueriesUpdate, type OnStoreChange, useLiveQueries} from './useLiveQueries'\nimport {usePerspective} from './usePerspective'\nimport {getQueryCacheKey, type QueryCacheKey} from './utils'\n\n/**\n * @internal\n */\nexport default function LiveStoreProvider(props: LiveQueryProviderProps): React.JSX.Element {\n const {children, token} = props\n\n if (!props.client) {\n throw new Error('Missing a `client` prop with a configured Sanity client instance')\n }\n\n const perspective = useQueryPerspective(usePerspective(props.perspective || 'drafts'))\n\n // Ensure these values are stable even if userland isn't memoizing properly\n const [client] = useState(() => {\n const {requestTagPrefix} = props.client.config()\n return props.client.withConfig({\n requestTagPrefix: requestTagPrefix || DEFAULT_TAG,\n // Set the recommended defaults, this is a convenience to make it easier to share a client config from a server component to the client component\n ...(token && {\n token,\n useCdn: false,\n perspective: 'drafts',\n ignoreBrowserTokenWarning: true,\n }),\n })\n })\n const [logger] = useState(() => props.logger)\n\n useEffect(() => {\n if (logger) {\n logger.log(\n `[@sanity/preview-kit]: Updates will be applied in real-time using the Sanity Live Content API.`,\n )\n }\n }, [logger])\n\n const {queries, snapshots, subscribe, update} = useLiveQueries()\n\n const context = useMemo(() => {\n return function defineListener<QueryResult>(\n initialSnapshot: QueryResult,\n query: string,\n params: QueryParams,\n hookPerspective: Exclude<ClientPerspective, 'raw'> | null,\n ) {\n const effectivePerspective = hookPerspective || perspective\n const snapshotsKey = getQueryCacheKey(query, params, effectivePerspective)\n const contextSubscribe: ListenerSubscribe = (onStoreChange) => {\n const unsubscribe = subscribe({\n query,\n params,\n perspective: effectivePerspective,\n onStoreChange,\n })\n\n return () => unsubscribe()\n }\n const getSnapshot: ListenerGetSnapshot<QueryResult> = () =>\n snapshots.has(snapshotsKey)\n ? (snapshots.get(snapshotsKey)?.result as unknown as QueryResult)\n : initialSnapshot\n\n return {subscribe: contextSubscribe, getSnapshot}\n } satisfies DefineListenerContext\n }, [perspective, snapshots, subscribe])\n\n const liveEvents = useLiveEvents(client)\n\n return (\n <Context.Provider value={context}>\n {children}\n {[...queries.entries()].map(([key, {query, params, perspective, listeners}]) => {\n return (\n <QuerySubscription\n key={`${liveEvents.resets}:${key}`}\n client={client}\n listeners={listeners}\n params={params}\n query={query}\n perspective={perspective}\n liveEventsMessages={liveEvents.messages}\n snapshotKey={key}\n syncTags={snapshots.get(key)?.syncTags}\n update={update}\n />\n )\n })}\n </Context.Provider>\n )\n}\nLiveStoreProvider.displayName = 'LiveStoreProvider'\n\ninterface QuerySubscriptionProps extends Required<Pick<LiveQueryProviderProps, 'client'>> {\n query: string\n params: QueryParams\n perspective: Exclude<ClientPerspective, 'raw'>\n update: LiveQueriesUpdate\n snapshotKey: QueryCacheKey\n liveEventsMessages: LiveEventMessage[]\n syncTags: SyncTag[] | undefined\n listeners: Set<OnStoreChange>\n}\nfunction QuerySubscription(props: QuerySubscriptionProps) {\n const {\n client,\n query,\n params,\n perspective,\n snapshotKey,\n update,\n liveEventsMessages,\n syncTags,\n listeners,\n } = props\n\n const [skipEventIds] = useState(() => new Set(liveEventsMessages.map((msg) => msg.id)))\n const recentLiveEvents = useMemo(\n () => liveEventsMessages.filter((msg) => !skipEventIds.has(msg.id)),\n [liveEventsMessages, skipEventIds],\n )\n const lastLiveEvent = useMemo(\n () => recentLiveEvents.findLast((msg) => msg.tags.some((tag) => syncTags?.includes(tag))),\n [recentLiveEvents, syncTags],\n )\n const lastLiveEventId = lastLiveEvent?.id\n\n // Make sure any async errors bubble up to the nearest error boundary\n const [error, setError] = useState<unknown>(null)\n if (error) throw error\n\n const shouldPause = useShouldPause()\n useEffect(() => {\n if (shouldPause) {\n return\n }\n let fulfilled = false\n const controller = new AbortController()\n\n client\n .fetch(query, params, {\n lastLiveEventId,\n perspective,\n signal: controller.signal,\n filterResponse: false,\n returnQuery: false,\n })\n .then(({result, resultSourceMap, syncTags: nextTags}) => {\n update(snapshotKey, result, resultSourceMap, nextTags)\n for (const listener of listeners) {\n listener()\n }\n fulfilled = true\n })\n .catch((error) => {\n if (error.name !== 'AbortError') {\n setError(error)\n }\n })\n\n return () => {\n if (!fulfilled) {\n controller.abort()\n }\n }\n }, [\n client,\n lastLiveEventId,\n listeners,\n params,\n perspective,\n query,\n shouldPause,\n snapshotKey,\n update,\n ])\n\n return null\n}\nQuerySubscription.displayName = 'QuerySubscription'\n"],"names":["reducer","useReducer","useState","useEffect","useDeferredValue","subscribe","useCallback","isEqual","comlink","createNode","createNodeMachine","createCompatibilityActors","useQueryPerspective","useMemo","unsubscribe","jsxs","Context","perspective","jsx","useShouldPause","error"],"mappings":"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"}