|
1 | | -import {type ResponseQueryOptions} from '@sanity/client' |
| 1 | +import {CorsOriginError, type ResponseQueryOptions} from '@sanity/client' |
2 | 2 | import {type SanityQueryResult} from 'groq' |
3 | 3 | import { |
4 | 4 | catchError, |
5 | 5 | combineLatest, |
| 6 | + defer, |
6 | 7 | distinctUntilChanged, |
7 | 8 | EMPTY, |
8 | 9 | filter, |
@@ -207,7 +208,18 @@ const listenToLiveClientAndSetLastLiveEventIds = ({ |
207 | 208 | apiVersion: QUERY_STORE_API_VERSION, |
208 | 209 | }).observable.pipe( |
209 | 210 | switchMap((client) => |
210 | | - client.live.events({includeDrafts: !!client.config().token, tag: 'query-store'}), |
| 211 | + defer(() => |
| 212 | + client.live.events({includeDrafts: !!client.config().token, tag: 'query-store'}), |
| 213 | + ).pipe( |
| 214 | + catchError((error) => { |
| 215 | + if (error instanceof CorsOriginError) { |
| 216 | + // Swallow only CORS errors in store without bubbling up so that they are handled by the Cors Error component |
| 217 | + state.set('setError', {error}) |
| 218 | + return EMPTY |
| 219 | + } |
| 220 | + throw error |
| 221 | + }), |
| 222 | + ), |
211 | 223 | ), |
212 | 224 | share(), |
213 | 225 | filter((e) => e.type === 'message'), |
@@ -305,6 +317,29 @@ const _getQueryState = bindActionByDataset( |
305 | 317 | }), |
306 | 318 | ) |
307 | 319 |
|
| 320 | +/** |
| 321 | + * Returns a state source for the top-level query store error (if any). |
| 322 | + * |
| 323 | + * Unlike {@link getQueryState}, this selector does not throw; it simply returns the error value. |
| 324 | + * Subscribe to this to be notified when a global query error occurs (e.g., CORS failures). |
| 325 | + * |
| 326 | + * @beta |
| 327 | + */ |
| 328 | +export function getQueryErrorState(instance: SanityInstance): StateSource<unknown | undefined> { |
| 329 | + return _getQueryErrorState(instance) |
| 330 | +} |
| 331 | + |
| 332 | +const _getQueryErrorState = bindActionByDataset( |
| 333 | + queryStore, |
| 334 | + createStateSourceAction({ |
| 335 | + selector: ({state}: SelectorContext<QueryStoreState>) => state.error, |
| 336 | + onSubscribe: () => { |
| 337 | + // No-op subscription as we don't track per-query subscribers here |
| 338 | + return () => {} |
| 339 | + }, |
| 340 | + }), |
| 341 | +) |
| 342 | + |
308 | 343 | /** |
309 | 344 | * Resolves the result of a query without registering a lasting subscriber. |
310 | 345 | * |
@@ -378,3 +413,16 @@ const _resolveQuery = bindActionByDataset( |
378 | 413 | return firstValueFrom(race([resolved$, aborted$])) |
379 | 414 | }, |
380 | 415 | ) |
| 416 | + |
| 417 | +/** |
| 418 | + * Clears the top-level query store error. |
| 419 | + * @beta |
| 420 | + */ |
| 421 | +export function clearQueryError(instance: SanityInstance): void |
| 422 | +export function clearQueryError(...args: Parameters<typeof _clearQueryError>): void { |
| 423 | + return _clearQueryError(...args) |
| 424 | +} |
| 425 | + |
| 426 | +const _clearQueryError = bindActionByDataset(queryStore, ({state}) => { |
| 427 | + state.set('setError', {error: undefined}) |
| 428 | +}) |
0 commit comments