UNPKG

@sanity/preview-kit

Version:

General purpose utils for live content and visual editing

1 lines 20.5 kB
{"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 QueryParams} from '@sanity/client'\n\nexport type QueryCacheKey = `${string}:${string}`\n\nexport function getQueryCacheKey(query: string, params: QueryParams): QueryCacheKey {\n return `${query}:${JSON.stringify(params)}`\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 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 onStoreChange: OnStoreChange\n }\n}\ntype UnsubscribeAction = {\n type: 'unsubscribe'\n payload: {\n query: string\n params: QueryParams\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: ClientPerspective\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)\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 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)\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 {\n ClientPerspective,\n LiveEventMessage,\n QueryParams,\n SanityClient,\n SyncTag,\n} from '@sanity/client'\nimport {useEffect, useMemo, useState} from 'react'\n\nimport {defineStoreContext as Context} from '../context'\nimport {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 = 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 ) {\n const snapshotsKey = getQueryCacheKey(query, params)\n const contextSubscribe: ListenerSubscribe = (onStoreChange) => {\n const unsubscribe = subscribe({query, params, onStoreChange})\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 }, [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, listeners}]) => {\n return (\n <QuerySubscription\n key={`${liveEvents.resets}:${perspective}:${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 as SanityClient)\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","useMemo","unsubscribe","jsxs","Context","jsx","useShouldPause","error"],"mappings":"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"}