From 1b934f60ad51b50b507849e75a0e1f0a1ce7c805 Mon Sep 17 00:00:00 2001 From: Carolina Gonzalez Date: Thu, 11 Sep 2025 11:12:19 -0400 Subject: [PATCH] feat(core): add media library instances and action bindings --- apps/kitchensink-react/src/AppRoutes.tsx | 7 +- .../src/routes/MediaLibraryRoute.tsx | 74 +++++++++++ apps/kitchensink-react/src/sanityConfigs.ts | 3 + packages/core/package.json | 2 +- packages/core/src/_exports/index.ts | 5 + packages/core/src/client/clientStore.ts | 28 +++- packages/core/src/config/handles.ts | 14 ++ packages/core/src/config/sanityConfig.ts | 32 ++++- packages/core/src/query/queryStore.ts | 36 ++++- .../core/src/releases/getPerspectiveState.ts | 4 +- .../core/src/store/createActionBinder.test.ts | 125 +++++++++++++++++- packages/core/src/store/createActionBinder.ts | 87 ++++++++++++ .../core/src/store/createSanityInstance.ts | 37 ++++-- packages/react/package.json | 2 +- .../src/hooks/context/useSanityInstance.ts | 9 ++ packages/react/src/hooks/query/useQuery.ts | 19 ++- pnpm-lock.yaml | 21 ++- 17 files changed, 472 insertions(+), 33 deletions(-) create mode 100644 apps/kitchensink-react/src/routes/MediaLibraryRoute.tsx diff --git a/apps/kitchensink-react/src/AppRoutes.tsx b/apps/kitchensink-react/src/AppRoutes.tsx index 45e6aa023..9d658823a 100644 --- a/apps/kitchensink-react/src/AppRoutes.tsx +++ b/apps/kitchensink-react/src/AppRoutes.tsx @@ -17,8 +17,9 @@ import {ProtectedRoute} from './ProtectedRoute' import {DashboardContextRoute} from './routes/DashboardContextRoute' import {DashboardWorkspacesRoute} from './routes/DashboardWorkspacesRoute' import ExperimentalResourceClientRoute from './routes/ExperimentalResourceClientRoute' -import {ProjectsRoute} from './routes/ProjectsRoute' +import MediaLibraryRoute from './routes/MediaLibraryRoute' import {PerspectivesRoute} from './routes/PerspectivesRoute' +import {ProjectsRoute} from './routes/ProjectsRoute' import {ReleasesRoute} from './routes/releases/ReleasesRoute' import {UserDetailRoute} from './routes/UserDetailRoute' import {UsersRoute} from './routes/UsersRoute' @@ -68,6 +69,10 @@ const documentCollectionRoutes = [ path: 'experimental-resource-client', element: , }, + { + path: 'media-library', + element: , + }, { path: 'presence', element: , diff --git a/apps/kitchensink-react/src/routes/MediaLibraryRoute.tsx b/apps/kitchensink-react/src/routes/MediaLibraryRoute.tsx new file mode 100644 index 000000000..416f1a2af --- /dev/null +++ b/apps/kitchensink-react/src/routes/MediaLibraryRoute.tsx @@ -0,0 +1,74 @@ +import {useQuery} from '@sanity/sdk-react' +import {Card, Spinner, Text} from '@sanity/ui' +import {type JSX, useState} from 'react' + +export default function MediaLibraryRoute(): JSX.Element { + const [query] = useState('*[_type == "sanity.asset"][0...10] | order(_id desc)') + const [isLoading] = useState(false) + + // for now, hardcoded. should be inferred from org later on + const {data, isPending} = useQuery({ + mediaLibraryId: 'mlPGY7BEqt52', + query, + }) + + return ( +
+ + Media Library Query Demo + + + + This route demonstrates querying against a Sanity media library. The MediaLibraryProvider is + automatically created by SanityApp when a media library config is present. The query runs + against: https://api.sanity.io/v2025-03-24/media-libraries/mlPGY7BEqt52/query + + + +
+ + Current query: + + + {query} + +
+
+ + +
+ + Query Results: + + {(isPending || isLoading) && } +
+ +
+          {JSON.stringify(data, null, 2)}
+        
+
+
+ ) +} diff --git a/apps/kitchensink-react/src/sanityConfigs.ts b/apps/kitchensink-react/src/sanityConfigs.ts index 998eaa007..27dc85ba9 100644 --- a/apps/kitchensink-react/src/sanityConfigs.ts +++ b/apps/kitchensink-react/src/sanityConfigs.ts @@ -13,6 +13,9 @@ export const devConfigs: SanityConfig[] = [ projectId: 'v28v5k8m', dataset: 'production', }, + { + mediaLibraryId: 'mlPGY7BEqt52', + }, ] export const e2eConfigs: SanityConfig[] = [ diff --git a/packages/core/package.json b/packages/core/package.json index 4d044ac16..beb53ebfc 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -57,7 +57,7 @@ "prettier": "@sanity/prettier-config", "dependencies": { "@sanity/bifur-client": "^0.4.1", - "@sanity/client": "^7.10.0", + "@sanity/client": "7.11.1-allow-live.0", "@sanity/comlink": "^3.0.4", "@sanity/diff-match-patch": "^3.2.0", "@sanity/diff-patch": "^6.0.0", diff --git a/packages/core/src/_exports/index.ts b/packages/core/src/_exports/index.ts index d052c8e2a..50ee10736 100644 --- a/packages/core/src/_exports/index.ts +++ b/packages/core/src/_exports/index.ts @@ -47,12 +47,14 @@ export { createDatasetHandle, createDocumentHandle, createDocumentTypeHandle, + createMediaLibraryHandle, createProjectHandle, } from '../config/handles' export { type DatasetHandle, type DocumentHandle, type DocumentTypeHandle, + type MediaLibraryHandle, type PerspectiveHandle, type ProjectHandle, type ReleasePerspective, @@ -125,6 +127,8 @@ export {getProjectsState, resolveProjects} from '../projects/projects' export { getQueryKey, getQueryState, + type MediaLibraryQueryOptions, + type MediaLibraryResolveQueryOptions, parseQueryKey, type QueryOptions, resolveQuery, @@ -132,6 +136,7 @@ export { export {getPerspectiveState} from '../releases/getPerspectiveState' export type {ReleaseDocument} from '../releases/releasesStore' export {getActiveReleasesState} from '../releases/releasesStore' +export {bindActionByMediaLibrary, bindActionByResource} from '../store/createActionBinder' export {createSanityInstance, type SanityInstance} from '../store/createSanityInstance' export {type Selector, type StateSource} from '../store/createStateSourceAction' export {getUsersKey, parseUsersKey} from '../users/reducers' diff --git a/packages/core/src/client/clientStore.ts b/packages/core/src/client/clientStore.ts index c83b266e0..b8d68ba1e 100644 --- a/packages/core/src/client/clientStore.ts +++ b/packages/core/src/client/clientStore.ts @@ -34,6 +34,7 @@ const allowedKeys = Object.keys({ 'maxRetries': null, 'dataset': null, 'projectId': null, + 'mediaLibraryId': null, 'scope': null, 'apiVersion': null, 'requestTagPrefix': null, @@ -86,6 +87,10 @@ export interface ClientOptions extends Pick(handle: DatasetHandle): DatasetHandle { return handle } + +/** + * Creates or validates a `MediaLibraryHandle` object. + * Ensures the provided object conforms to the `MediaLibraryHandle` interface. + * @param handle - The object containing media library identification properties. + * @returns The validated `MediaLibraryHandle` object. + * @public + */ +export function createMediaLibraryHandle( + handle: MediaLibraryHandle, +): MediaLibraryHandle { + return handle +} diff --git a/packages/core/src/config/sanityConfig.ts b/packages/core/src/config/sanityConfig.ts index 7dbd8accd..b098e91a8 100644 --- a/packages/core/src/config/sanityConfig.ts +++ b/packages/core/src/config/sanityConfig.ts @@ -34,6 +34,15 @@ export interface DatasetHandle + extends PerspectiveHandle { + mediaLibraryId?: TMediaLibraryId +} + /** * Identifies a specific document type within a Sanity dataset and project. * Includes `projectId`, `dataset`, and `documentType`. @@ -67,7 +76,12 @@ export interface DocumentHandle< * Represents the complete configuration for a Sanity SDK instance * @public */ -export interface SanityConfig extends DatasetHandle, PerspectiveHandle { +export interface SanityConfig extends DatasetHandle, PerspectiveHandle, MediaLibraryHandle { + /** + * Media library ID for media library queries + * @remarks When provided, enables media library functionality + */ + mediaLibraryId?: string /** * Authentication configuration for the instance * @remarks Merged with parent configurations when using createChild @@ -81,3 +95,19 @@ export interface SanityConfig extends DatasetHandle, PerspectiveHandle { enabled: boolean } } + +// /** +// * Configuration for a Sanity SDK instance with dataset support +// * @public +// */ +// export interface DatasetSanityConfig extends SanityConfig {} + +// /** +// * Configuration for a Sanity SDK instance with media library support +// * @public +// */ +// export interface MediaLibrarySanityConfig extends SanityConfig { +// mediaLibraryId: string +// projectId?: never +// dataset?: never +// } diff --git a/packages/core/src/query/queryStore.ts b/packages/core/src/query/queryStore.ts index fb1b15a6f..fe09202ef 100644 --- a/packages/core/src/query/queryStore.ts +++ b/packages/core/src/query/queryStore.ts @@ -22,9 +22,9 @@ import { } from 'rxjs' import {getClientState} from '../client/clientStore' -import {type DatasetHandle} from '../config/sanityConfig' +import {type DatasetHandle, type MediaLibraryHandle} from '../config/sanityConfig' import {getPerspectiveState} from '../releases/getPerspectiveState' -import {bindActionByDataset} from '../store/createActionBinder' +import {bindActionByResource} from '../store/createActionBinder' import {type SanityInstance} from '../store/createSanityInstance' import { createStateSourceAction, @@ -63,6 +63,18 @@ export interface QueryOptions< params?: Record } +/** + * @beta + */ +export interface MediaLibraryQueryOptions< + TQuery extends string = string, + TMediaLibraryId extends string = string, +> extends Pick, + MediaLibraryHandle { + query: TQuery + params?: Record +} + /** * @beta */ @@ -74,6 +86,16 @@ export interface ResolveQueryOptions< signal?: AbortSignal } +/** + * @beta + */ +export interface MediaLibraryResolveQueryOptions< + TQuery extends string = string, + TMediaLibraryId extends string = string, +> extends MediaLibraryQueryOptions { + signal?: AbortSignal +} + const EMPTY_ARRAY: never[] = [] /** @beta */ @@ -206,9 +228,9 @@ const listenToLiveClientAndSetLastLiveEventIds = ({ const liveMessages$ = getClientState(instance, { apiVersion: QUERY_STORE_API_VERSION, }).observable.pipe( - switchMap((client) => - client.live.events({includeDrafts: !!client.config().token, tag: 'query-store'}), - ), + switchMap((client) => { + return client.live.events({includeDrafts: !!client.config().token, tag: 'query-store'}) + }), share(), filter((e) => e.type === 'message'), ) @@ -278,7 +300,7 @@ export function getQueryState( ): ReturnType { return _getQueryState(...args) } -const _getQueryState = bindActionByDataset( +const _getQueryState = bindActionByResource( queryStore, createStateSourceAction({ selector: ({state, instance}: SelectorContext, options: QueryOptions) => { @@ -337,7 +359,7 @@ export function resolveQuery( export function resolveQuery(...args: Parameters): Promise { return _resolveQuery(...args) } -const _resolveQuery = bindActionByDataset( +const _resolveQuery = bindActionByResource( queryStore, ({state, instance}, {signal, ...options}: ResolveQueryOptions) => { const normalized = normalizeOptionsWithPerspective(instance, options) diff --git a/packages/core/src/releases/getPerspectiveState.ts b/packages/core/src/releases/getPerspectiveState.ts index 1ae3fb10a..093e4f004 100644 --- a/packages/core/src/releases/getPerspectiveState.ts +++ b/packages/core/src/releases/getPerspectiveState.ts @@ -1,7 +1,7 @@ import {createSelector} from 'reselect' import {type PerspectiveHandle, type ReleasePerspective} from '../config/sanityConfig' -import {bindActionByDataset} from '../store/createActionBinder' +import {bindActionByResource} from '../store/createActionBinder' import {createStateSourceAction, type SelectorContext} from '../store/createStateSourceAction' import {releasesStore, type ReleasesStoreState} from './releasesStore' import {sortReleases} from './utils/sortReleases' @@ -62,7 +62,7 @@ const memoizedOptionsSelector = createSelector( * * @public */ -export const getPerspectiveState = bindActionByDataset( +export const getPerspectiveState = bindActionByResource( releasesStore, createStateSourceAction({ selector: createSelector( diff --git a/packages/core/src/store/createActionBinder.test.ts b/packages/core/src/store/createActionBinder.test.ts index 57bd46e95..1d557fec6 100644 --- a/packages/core/src/store/createActionBinder.test.ts +++ b/packages/core/src/store/createActionBinder.test.ts @@ -1,6 +1,12 @@ import {beforeEach, describe, expect, it, vi} from 'vitest' -import {bindActionByDataset, bindActionGlobally, createActionBinder} from './createActionBinder' +import { + bindActionByDataset, + bindActionByMediaLibrary, + bindActionByResource, + bindActionGlobally, + createActionBinder, +} from './createActionBinder' import {createSanityInstance} from './createSanityInstance' import {createStoreInstance} from './createStoreInstance' @@ -115,6 +121,123 @@ describe('bindActionByDataset', () => { }) }) +describe('bindActionByMediaLibrary', () => { + it('should work correctly when mediaLibraryId is provided', () => { + const storeDefinition = { + name: 'MediaStore', + getInitialState: () => ({assets: {}}), + } + const action = vi.fn((_context, value: string) => value) + const boundAction = bindActionByMediaLibrary(storeDefinition, action) + const instance = createSanityInstance({mediaLibraryId: 'lib123'}) + const result = boundAction(instance, 'hello') + expect(result).toBe('hello') + }) + + it('should throw an error if mediaLibraryId is missing', () => { + const storeDefinition = { + name: 'MediaStore', + getInitialState: () => ({assets: {}}), + } + const action = vi.fn((_context) => 'fail') + const boundAction = bindActionByMediaLibrary(storeDefinition, action) + // Instance with missing mediaLibraryId + const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'}) + expect(() => boundAction(instance)).toThrow('This API requires a media library ID configured.') + }) + + it('should create separate store instances for different media libraries', () => { + const storeDefinition = { + name: 'MediaStore', + getInitialState: () => ({assets: {}}), + } + const action = vi.fn((context, assetId: string) => { + // Initialize assets if not present + if (!context.state.assets) { + context.state.assets = {} + } + context.state.assets[assetId] = true + return Object.keys(context.state.assets).length + }) + const boundAction = bindActionByMediaLibrary(storeDefinition, action) + + const instance1 = createSanityInstance({mediaLibraryId: 'lib1'}) + const instance2 = createSanityInstance({mediaLibraryId: 'lib2'}) + + const result1 = boundAction(instance1, 'asset1') + const result2 = boundAction(instance2, 'asset2') + + expect(result1).toBe(1) + expect(result2).toBe(1) // Different media library, so separate state + expect(vi.mocked(createStoreInstance)).toHaveBeenCalledTimes(2) + }) +}) + +describe('bindActionByResource', () => { + it('should work correctly with dataset configuration', () => { + const storeDefinition = { + name: 'ResourceStore', + getInitialState: () => ({data: {}}), + } + const action = vi.fn((_context, value: string) => value) + const boundAction = bindActionByResource(storeDefinition, action) + const instance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'}) + const result = boundAction(instance, 'hello') + expect(result).toBe('hello') + }) + + it('should work correctly with media library configuration', () => { + const storeDefinition = { + name: 'ResourceStore', + getInitialState: () => ({data: {}}), + } + const action = vi.fn((_context, value: string) => value) + const boundAction = bindActionByResource(storeDefinition, action) + const instance = createSanityInstance({mediaLibraryId: 'lib123'}) + const result = boundAction(instance, 'hello') + expect(result).toBe('hello') + }) + + it('should throw an error if neither dataset nor media library is configured', () => { + const storeDefinition = { + name: 'ResourceStore', + getInitialState: () => ({data: {}}), + } + const action = vi.fn((_context) => 'fail') + const boundAction = bindActionByResource(storeDefinition, action) + // Instance with no valid configuration + const instance = createSanityInstance({}) + expect(() => boundAction(instance)).toThrow( + 'This API requires either a project ID and dataset, or a media library ID configured.', + ) + }) + + it.skip('should create separate store instances for different resource types', () => { + const storeDefinition = { + name: 'ResourceStore', + getInitialState: () => ({data: {}}), + } + const action = vi.fn((context, key: string) => { + if (!context.state.data) { + context.state.data = {} + } + context.state.data[key] = true + return Object.keys(context.state.data).length + }) + const boundAction = bindActionByResource(storeDefinition, action) + + const datasetInstance = createSanityInstance({projectId: 'proj1', dataset: 'ds1'}) + const mediaLibraryInstance = createSanityInstance({mediaLibraryId: 'lib1'}) + + const result1 = boundAction(datasetInstance, 'item1') + const result2 = boundAction(mediaLibraryInstance, 'item2') + + expect(result1).toBe(1) + expect(result2).toBe(1) // Different resource type, so separate state + expect(vi.mocked(createStoreInstance)).toHaveBeenCalledTimes(2) + }) +}) + describe('bindActionGlobally', () => { it('should work correctly ignoring config in key generation', () => { const storeDefinition = { diff --git a/packages/core/src/store/createActionBinder.ts b/packages/core/src/store/createActionBinder.ts index 4d505707e..61282222c 100644 --- a/packages/core/src/store/createActionBinder.ts +++ b/packages/core/src/store/createActionBinder.ts @@ -135,6 +135,93 @@ export const bindActionByDataset = createActionBinder(({projectId, dataset}) => return `${projectId}.${dataset}` }) +/** + * @public + * Binds an action to a store that's scoped to a specific media library + * + * @remarks + * This creates actions that operate on state isolated to a specific media library. + * Different media libraries will have separate states. + * + * @throws Error if mediaLibraryId is missing from the Sanity instance config + * + * @example + * ```ts + * // Define a store + * const mediaStore = defineStore({ + * name: 'Media', + * getInitialState: () => ({ assets: {} }), + * // ... + * }) + * + * // Create media library-specific actions + * export const fetchAsset = bindActionByMediaLibrary( + * mediaStore, + * ({instance, state}, assetId) => { + * // This state is isolated to the specific media library + * // ...fetch logic... + * } + * ) + * + * // Usage + * fetchAsset(sanityInstance, 'asset123') + * ``` + */ +export const bindActionByMediaLibrary = createActionBinder(({mediaLibraryId}) => { + if (!mediaLibraryId) { + throw new Error('This API requires a media library ID configured.') + } + return `media-library:${mediaLibraryId}` +}) + +/** + * @public + * Binds an action to a store that's scoped to either a dataset or media library + * + * @remarks + * This creates actions that can operate on state isolated to either a specific project/dataset + * combination or a specific media library, depending on the configuration provided. + * Different resource types will have separate states. + * + * @throws Error if neither projectId/dataset nor mediaLibraryId is provided + * + * @example + * ```ts + * // Define a store + * const queryStore = defineStore({ + * name: 'Query', + * getInitialState: () => ({ queries: {} }), + * // ... + * }) + * + * // Create universal actions that work with both datasets and media libraries + * export const executeQuery = bindActionByResource( + * queryStore, + * ({instance, state}, queryOptions) => { + * // This state is isolated to the specific resource (dataset or media library) + * // ...query logic... + * } + * ) + * + * // Usage with dataset + * executeQuery(datasetInstance, { projectId: 'proj', dataset: 'ds', query: '*' }) + * + * // Usage with media library + * executeQuery(mediaLibraryInstance, { mediaLibraryId: 'lib', query: '*' }) + * ``` + */ +export const bindActionByResource = createActionBinder((config) => { + if (config.mediaLibraryId && !config.projectId && !config.dataset) { + return `media-library:${config.mediaLibraryId}` + } + if (config.projectId && config.dataset) { + return `${config.projectId}.${config.dataset}` + } + throw new Error( + 'This API requires either a project ID and dataset, or a media library ID configured.', + ) +}) + /** * Binds an action to a global store that's shared across all Sanity instances * diff --git a/packages/core/src/store/createSanityInstance.ts b/packages/core/src/store/createSanityInstance.ts index e20fe0947..ca070b206 100644 --- a/packages/core/src/store/createSanityInstance.ts +++ b/packages/core/src/store/createSanityInstance.ts @@ -3,6 +3,9 @@ import {pick} from 'lodash-es' import {type SanityConfig} from '../config/sanityConfig' import {insecureRandomId} from '../utils/ids' +const isMediaLibrary = (config: SanityConfig) => + config.mediaLibraryId && !config.projectId && !config.dataset + /** * Represents a Sanity.io resource instance with its own configuration and lifecycle * @remarks Instances form a hierarchy through parent/child relationships @@ -94,23 +97,35 @@ export function createSanityInstance(config: SanityConfig = {}): SanityInstance } }, getParent: () => undefined, - createChild: (next) => - Object.assign( - createSanityInstance({ + createChild: (next) => { + const shouldMerge = isMediaLibrary(config) === isMediaLibrary(next) + let newInstanceConfig = next + + if (shouldMerge) { + // For dataset configs, merge with parent as usual + newInstanceConfig = { ...config, ...next, ...(config.auth === next.auth ? config.auth : config.auth && next.auth && {auth: {...config.auth, ...next.auth}}), - }), - {getParent: () => instance}, - ), + } + } + return Object.assign(createSanityInstance(newInstanceConfig), {getParent: () => instance}) + }, match: (targetConfig) => { - if ( - Object.entries(pick(targetConfig, 'auth', 'projectId', 'dataset')).every( - ([key, value]) => config[key as keyof SanityConfig] === value, - ) - ) { + // Check if this instance matches the target configuration + const targetEntries = Object.entries( + pick(targetConfig, 'auth', 'projectId', 'dataset', 'mediaLibraryId'), + ) + + // Check if all specified properties match + const matches = targetEntries.every(([key, value]) => { + const instanceValue = config[key as keyof SanityConfig] + return instanceValue === value + }) + + if (matches) { return instance } diff --git a/packages/react/package.json b/packages/react/package.json index 7965d0d27..df0db7a66 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -56,7 +56,7 @@ "browserslist": "extends @sanity/browserslist-config", "prettier": "@sanity/prettier-config", "dependencies": { - "@sanity/client": "^7.10.0", + "@sanity/client": "7.11.1-allow-live.0", "@sanity/message-protocol": "^0.12.0", "@sanity/sdk": "workspace:*", "@sanity/types": "^3.83.0", diff --git a/packages/react/src/hooks/context/useSanityInstance.ts b/packages/react/src/hooks/context/useSanityInstance.ts index 2abb631fb..9d7b77f53 100644 --- a/packages/react/src/hooks/context/useSanityInstance.ts +++ b/packages/react/src/hooks/context/useSanityInstance.ts @@ -22,6 +22,7 @@ import {SanityInstanceContext} from '../../context/SanityInstanceContext' * Use this hook when you need to: * - Access the current SanityInstance from context * - Find a specific instance with matching project/dataset configuration + * - Find a specific instance with matching media library configuration * - Access a parent instance with specific configuration values * * @example Get the current instance @@ -46,6 +47,14 @@ import {SanityInstanceContext} from '../../context/SanityInstanceContext' * } * ``` * + * @example Find an instance with media library configuration + * ```tsx + * // Find an instance matching the given media library + * const instance = useSanityInstance({ + * mediaLibraryId: 'al34RQcBfuD7' + * }) + * ``` + * * @example Match partial configuration * ```tsx * // Find an instance with specific auth configuration diff --git a/packages/react/src/hooks/query/useQuery.ts b/packages/react/src/hooks/query/useQuery.ts index b0e3612ac..657cf468d 100644 --- a/packages/react/src/hooks/query/useQuery.ts +++ b/packages/react/src/hooks/query/useQuery.ts @@ -1,6 +1,7 @@ import { getQueryKey, getQueryState, + type MediaLibraryQueryOptions, parseQueryKey, type QueryOptions, resolveQuery, @@ -115,6 +116,19 @@ export function useQuery(options: QueryOptions): { isPending: boolean } +/** + * @public + * Overload for media library queries + */ +export function useQuery( + options: MediaLibraryQueryOptions, +): { + /** The query result, typed based on the GROQ query string */ + data: SanityQueryResult + /** True if a query transition is in progress */ + isPending: boolean +} + /** * @public * Fetches data and subscribes to real-time updates using a GROQ query. @@ -133,7 +147,10 @@ export function useQuery(options: QueryOptions): { * * @category GROQ */ -export function useQuery(options: QueryOptions): {data: unknown; isPending: boolean} { +export function useQuery(options: QueryOptions | MediaLibraryQueryOptions): { + data: unknown + isPending: boolean +} { // Implementation returns unknown, overloads define specifics const instance = useSanityInstance(options) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1ced6812..d376918d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -342,8 +342,8 @@ importers: specifier: ^0.4.1 version: 0.4.1 '@sanity/client': - specifier: ^7.10.0 - version: 7.10.0(debug@4.4.1) + specifier: 7.11.1-allow-live.0 + version: 7.11.1-allow-live.0 '@sanity/comlink': specifier: ^3.0.4 version: 3.0.4 @@ -436,8 +436,8 @@ importers: packages/react: dependencies: '@sanity/client': - specifier: ^7.10.0 - version: 7.10.0(debug@4.4.1) + specifier: 7.11.1-allow-live.0 + version: 7.11.1-allow-live.0 '@sanity/message-protocol': specifier: ^0.12.0 version: 0.12.0 @@ -2730,6 +2730,10 @@ packages: resolution: {integrity: sha512-3UV6pJupue7UAZh4g5n0lYjuRsIb4c6IoqMywVLMHYoSOeHJfgSzbexOPGMNqvF+KUywUA0GyFi9cAVeMKofvw==} engines: {node: '>=20'} + '@sanity/client@7.11.1-allow-live.0': + resolution: {integrity: sha512-0erApHCvP5+299YggQKg/8CLGXWtYG2oFxXOiHkqwwllkRV2EWSnFGbsWjX5S+Os4IUauZUELL/NeNASYIj3Ug==} + engines: {node: '>=20'} + '@sanity/client@7.2.1': resolution: {integrity: sha512-BJ3RddlStGc+PjwBwhJIvWgXJbt3+TdWm/ywWWCNruqS5aqOSvvLUFjaAXp5ci4Wy8BL/RnZ60WIeqPi67+5xw==} engines: {node: '>=20'} @@ -10297,6 +10301,15 @@ snapshots: transitivePeerDependencies: - debug + '@sanity/client@7.11.1-allow-live.0': + dependencies: + '@sanity/eventsource': 5.0.2 + get-it: 8.6.10(debug@4.4.1) + nanoid: 3.3.11 + rxjs: 7.8.2 + transitivePeerDependencies: + - debug + '@sanity/client@7.2.1(debug@4.4.1)': dependencies: '@sanity/eventsource': 5.0.2