11import {
2+ type ClientConfig ,
23 type ClientPerspective ,
34 createClient ,
45 type SanityClient ,
@@ -12,6 +13,68 @@ import type { SanityConfig } from "./config";
1213 */
1314const cache = new Map < string , SanityClient > ( ) ;
1415
16+ /**
17+ * Tracks `<projectId>:<dataset>` pairs that returned HTTP 404 ("Dataset not found")
18+ * during the lifetime of the isolate so subsequent queries route directly to
19+ * `production` without an extra round-trip.
20+ */
21+ const missingDatasets = new Set < string > ( ) ;
22+
23+ function isDatasetNotFoundError ( err : unknown ) : boolean {
24+ if ( ! err || typeof err !== "object" ) {
25+ return false ;
26+ }
27+ const status = ( err as { statusCode ?: number } ) . statusCode ;
28+ const message = ( err as { message ?: string } ) . message || "" ;
29+ return (
30+ status === 404 &&
31+ ( message . includes ( "Dataset" ) || message . includes ( "not found" ) )
32+ ) ;
33+ }
34+
35+ function wrapWithDatasetFallback (
36+ primary : SanityClient ,
37+ getFallback : ( ) => SanityClient ,
38+ projectId : string ,
39+ dataset : string ,
40+ ) : SanityClient {
41+ if ( dataset === "production" ) {
42+ return primary ;
43+ }
44+ const datasetKey = `${ projectId } :${ dataset } ` ;
45+ const originalFetch = primary . fetch . bind ( primary ) ;
46+ primary . fetch = ( async (
47+ query : string ,
48+ params ?: Record < string , unknown > ,
49+ options ?: unknown ,
50+ ) => {
51+ if ( missingDatasets . has ( datasetKey ) ) {
52+ return ( getFallback ( ) . fetch as typeof originalFetch ) (
53+ query ,
54+ params as never ,
55+ options as never ,
56+ ) ;
57+ }
58+ try {
59+ return await originalFetch ( query , params as never , options as never ) ;
60+ } catch ( err ) {
61+ if ( isDatasetNotFoundError ( err ) ) {
62+ missingDatasets . add ( datasetKey ) ;
63+ console . warn (
64+ `[Sanity] Dataset "${ dataset } " not found for project "${ projectId } ", falling back to "production".` ,
65+ ) ;
66+ return ( getFallback ( ) . fetch as typeof originalFetch ) (
67+ query ,
68+ params as never ,
69+ options as never ,
70+ ) ;
71+ }
72+ throw err ;
73+ }
74+ } ) as typeof primary . fetch ;
75+ return primary ;
76+ }
77+
1578function memo ( key : string , make : ( ) => SanityClient ) : SanityClient {
1679 let client = cache . get ( key ) ;
1780 if ( ! client ) {
@@ -21,6 +84,28 @@ function memo(key: string, make: () => SanityClient): SanityClient {
2184 return client ;
2285}
2386
87+ /**
88+ * Creates a Sanity client that automatically falls back to `dataset: "production"`
89+ * if the configured dataset does not exist (HTTP 404).
90+ */
91+ export function createResilientClient ( config : ClientConfig ) : SanityClient {
92+ const projectId = config . projectId || "hfh83o0w" ;
93+ const dataset = config . dataset || "production" ;
94+ if (
95+ dataset !== "production" &&
96+ missingDatasets . has ( `${ projectId } :${ dataset } ` )
97+ ) {
98+ return createClient ( { ...config , dataset : "production" } ) ;
99+ }
100+ const primary = createClient ( config ) ;
101+ return wrapWithDatasetFallback (
102+ primary ,
103+ ( ) => createClient ( { ...config , dataset : "production" } ) ,
104+ projectId ,
105+ dataset ,
106+ ) ;
107+ }
108+
24109/**
25110 * Anonymous, published-only reads.
26111 *
@@ -35,16 +120,28 @@ function memo(key: string, make: () => SanityClient): SanityClient {
35120 */
36121export function publishedClient ( config : SanityConfig ) : SanityClient {
37122 const { projectId, dataset, apiVersion } = config ;
38- return memo ( `pub:${ projectId } :${ dataset } :${ apiVersion } ` , ( ) =>
39- createClient ( {
123+ if (
124+ dataset !== "production" &&
125+ missingDatasets . has ( `${ projectId } :${ dataset } ` )
126+ ) {
127+ return publishedClient ( { ...config , dataset : "production" } ) ;
128+ }
129+ return memo ( `pub:${ projectId } :${ dataset } :${ apiVersion } ` , ( ) => {
130+ const client = createClient ( {
40131 projectId,
41132 dataset,
42133 apiVersion,
43134 useCdn : true ,
44135 perspective : "published" ,
45136 stega : false ,
46- } ) ,
47- ) ;
137+ } ) ;
138+ return wrapWithDatasetFallback (
139+ client ,
140+ ( ) => publishedClient ( { ...config , dataset : "production" } ) ,
141+ projectId ,
142+ dataset ,
143+ ) ;
144+ } ) ;
48145}
49146
50147/** Authenticated draft/preview reads, with stega and source maps enabled. */
@@ -54,9 +151,19 @@ export function previewClient(
54151 perspective : ClientPerspective ,
55152) : SanityClient {
56153 const { projectId, dataset, apiVersion, studioUrl } = config ;
154+ if (
155+ dataset !== "production" &&
156+ missingDatasets . has ( `${ projectId } :${ dataset } ` )
157+ ) {
158+ return previewClient (
159+ { ...config , dataset : "production" } ,
160+ token ,
161+ perspective ,
162+ ) ;
163+ }
57164 const key = `prev:${ projectId } :${ dataset } :${ apiVersion } :${ String ( perspective ) } ` ;
58- return memo ( key , ( ) =>
59- createClient ( {
165+ return memo ( key , ( ) => {
166+ const client = createClient ( {
60167 projectId,
61168 dataset,
62169 apiVersion,
@@ -65,6 +172,13 @@ export function previewClient(
65172 token,
66173 resultSourceMap : "withKeyArraySelector" ,
67174 stega : { enabled : true , studioUrl } ,
68- } ) ,
69- ) ;
175+ } ) ;
176+ return wrapWithDatasetFallback (
177+ client ,
178+ ( ) =>
179+ previewClient ( { ...config , dataset : "production" } , token , perspective ) ,
180+ projectId ,
181+ dataset ,
182+ ) ;
183+ } ) ;
70184}
0 commit comments