@sanity/client 6.28.3-instruct.1 → 6.28.3-resources.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,5 @@
1
1
  import {lastValueFrom, Observable} from 'rxjs'
2
2
 
3
- import {AiClient, ObservableAiClient} from './ai/AiClient'
4
3
  import {AssetsClient, ObservableAssetsClient} from './assets/AssetsClient'
5
4
  import {defaultConfig, initConfig} from './config'
6
5
  import * as dataMethods from './data/dataMethods'
@@ -66,7 +65,7 @@ export class ObservableSanityClient {
66
65
  live: LiveClient
67
66
  projects: ObservableProjectsClient
68
67
  users: ObservableUsersClient
69
- ai: ObservableAiClient
68
+
70
69
  /**
71
70
  * Private properties
72
71
  */
@@ -88,7 +87,6 @@ export class ObservableSanityClient {
88
87
  this.live = new LiveClient(this)
89
88
  this.projects = new ObservableProjectsClient(this, this.#httpRequest)
90
89
  this.users = new ObservableUsersClient(this, this.#httpRequest)
91
- this.ai = new ObservableAiClient(this, this.#httpRequest)
92
90
  }
93
91
 
94
92
  /**
@@ -735,7 +733,6 @@ export class SanityClient {
735
733
  live: LiveClient
736
734
  projects: ProjectsClient
737
735
  users: UsersClient
738
- ai: AiClient
739
736
 
740
737
  /**
741
738
  * Observable version of the Sanity client, with the same configuration as the promise-based one
@@ -763,7 +760,6 @@ export class SanityClient {
763
760
  this.live = new LiveClient(this)
764
761
  this.projects = new ProjectsClient(this, this.#httpRequest)
765
762
  this.users = new UsersClient(this, this.#httpRequest)
766
- this.ai = new AiClient(this, this.#httpRequest)
767
763
 
768
764
  this.observable = new ObservableSanityClient(httpRequest, config)
769
765
  }
@@ -7,6 +7,7 @@ import type {
7
7
  Any,
8
8
  HttpRequest,
9
9
  HttpRequestEvent,
10
+ InitializedClientConfig,
10
11
  ResponseEvent,
11
12
  SanityAssetDocument,
12
13
  SanityImageAssetDocument,
@@ -149,8 +150,7 @@ function _upload(
149
150
  meta = ['none']
150
151
  }
151
152
 
152
- const dataset = validators.hasDataset(client.config())
153
- const assetEndpoint = assetType === 'image' ? 'images' : 'files'
153
+ const config = client.config()
154
154
  const options = optionsFromFile(opts, body)
155
155
  const {tag, label, title, description, creditLine, filename, source} = options
156
156
  const query: Any = {
@@ -166,17 +166,29 @@ function _upload(
166
166
  query.sourceName = source.name
167
167
  query.sourceUrl = source.url
168
168
  }
169
+
169
170
  return _requestObservable(client, httpRequest, {
170
171
  tag,
171
172
  method: 'POST',
172
173
  timeout: options.timeout || 0,
173
- uri: `/assets/${assetEndpoint}/${dataset}`,
174
+ uri: buildAssetUploadUrl(config, assetType),
174
175
  headers: options.contentType ? {'Content-Type': options.contentType} : {},
175
176
  query,
176
177
  body,
177
178
  })
178
179
  }
179
180
 
181
+ function buildAssetUploadUrl(config: InitializedClientConfig, assetType: 'image' | 'file'): string {
182
+ const assetTypeEndpoint = assetType === 'image' ? 'images' : 'files'
183
+
184
+ if (config['~experimental_resource']) {
185
+ return `${config['~experimental_resource'].type}/${config['~experimental_resource'].id}/assets/${assetTypeEndpoint}`
186
+ }
187
+
188
+ const dataset = validators.hasDataset(config)
189
+ return `assets/${assetTypeEndpoint}/${dataset}`
190
+ }
191
+
180
192
  function optionsFromFile(opts: Record<string, Any>, file: Any) {
181
193
  if (typeof File === 'undefined' || !(file instanceof File)) {
182
194
  return opts
package/src/config.ts CHANGED
@@ -63,7 +63,7 @@ export const initConfig = (
63
63
  ...defaultConfig,
64
64
  ...specifiedConfig,
65
65
  } as InitializedClientConfig
66
- const projectBased = newConfig.useProjectHostname
66
+ const projectBased = newConfig.useProjectHostname && !newConfig['~experimental_resource']
67
67
 
68
68
  if (typeof Promise === 'undefined') {
69
69
  const helpUrl = generateHelpUrl('js-client-promise-polyfill')
@@ -74,6 +74,10 @@ export const initConfig = (
74
74
  throw new Error('Configuration must contain `projectId`')
75
75
  }
76
76
 
77
+ if (newConfig['~experimental_resource']) {
78
+ validate.resourceBase(newConfig)
79
+ }
80
+
77
81
  if (typeof newConfig.perspective !== 'undefined') {
78
82
  validateApiPerspective(newConfig.perspective)
79
83
  }
@@ -151,7 +155,7 @@ export const initConfig = (
151
155
  const host = hostParts[1]
152
156
  const cdnHost = newConfig.isDefaultApi ? defaultCdnHost : host
153
157
 
154
- if (newConfig.useProjectHostname) {
158
+ if (projectBased) {
155
159
  newConfig.url = `${protocol}://${newConfig.projectId}.${host}/v${newConfig.apiVersion}`
156
160
  newConfig.cdnUrl = `${protocol}://${newConfig.projectId}.${cdnHost}/v${newConfig.apiVersion}`
157
161
  } else {
@@ -12,6 +12,7 @@ import type {
12
12
  Any,
13
13
  BaseActionOptions,
14
14
  BaseMutationOptions,
15
+ ClientConfig,
15
16
  FirstDocumentIdMutationOptions,
16
17
  FirstDocumentMutationOptions,
17
18
  HttpRequest,
@@ -366,6 +367,41 @@ export function _create<R extends Record<string, Any>>(
366
367
  const opts = Object.assign({returnFirst: true, returnDocuments: true}, options)
367
368
  return _dataRequest(client, httpRequest, 'mutate', {mutations: [mutation]}, opts)
368
369
  }
370
+ const resourceDatasetRegex = /^\/projects\/([^/]+)\/datasets\/([^/]+)\/([^/^?]+)\/?/
371
+ const isResourceDataEndpoint = (config: ClientConfig, uri: string, endpoint: string) => {
372
+ if (!config['~experimental_resource']) {
373
+ return false
374
+ }
375
+ if (
376
+ uri.startsWith(
377
+ `/${config['~experimental_resource'].type}/${config['~experimental_resource'].id}/${endpoint}`,
378
+ )
379
+ ) {
380
+ return true
381
+ }
382
+ const match = uri.match(resourceDatasetRegex)
383
+ if (!match) {
384
+ return false
385
+ }
386
+ return match[3] === endpoint
387
+ }
388
+ const isQuery = (config: ClientConfig, uri: string) =>
389
+ uri.startsWith('/data/query/') || isResourceDataEndpoint(config, uri, 'query')
390
+ const isMutate = (config: ClientConfig, uri: string) =>
391
+ uri.startsWith('/data/mutate/') || isResourceDataEndpoint(config, uri, 'mutate')
392
+ const isDoc = (config: ClientConfig, uri: string) =>
393
+ uri.startsWith('/data/doc/') || isResourceDataEndpoint(config, uri, 'doc')
394
+ const isListener = (config: ClientConfig, uri: string) =>
395
+ uri.startsWith('/data/listen/') || isResourceDataEndpoint(config, uri, 'listen')
396
+ const isHistory = (config: ClientConfig, uri: string) =>
397
+ uri.startsWith('/data/history/') || isResourceDataEndpoint(config, uri, 'history')
398
+ const isData = (config: ClientConfig, uri: string) =>
399
+ uri.startsWith('/data/') ||
400
+ isQuery(config, uri) ||
401
+ isMutate(config, uri) ||
402
+ isDoc(config, uri) ||
403
+ isListener(config, uri) ||
404
+ isHistory(config, uri)
369
405
 
370
406
  /**
371
407
  * @internal
@@ -382,7 +418,7 @@ export function _requestObservable<R>(
382
418
  // Only the /data endpoint is currently available through API-CDN.
383
419
  const canUseCdn =
384
420
  typeof options.canUseCdn === 'undefined'
385
- ? ['GET', 'HEAD'].indexOf(options.method || 'GET') >= 0 && uri.indexOf('/data/') === 0
421
+ ? ['GET', 'HEAD'].indexOf(options.method || 'GET') >= 0 && isData(config, uri)
386
422
  : options.canUseCdn
387
423
 
388
424
  let useCdn = (options.useCdn ?? config.useCdn) && canUseCdn
@@ -397,10 +433,7 @@ export function _requestObservable<R>(
397
433
  }
398
434
 
399
435
  // GROQ query-only parameters
400
- if (
401
- ['GET', 'HEAD', 'POST'].indexOf(options.method || 'GET') >= 0 &&
402
- uri.indexOf('/data/query/') === 0
403
- ) {
436
+ if (['GET', 'HEAD', 'POST'].indexOf(options.method || 'GET') >= 0 && isQuery(config, uri)) {
404
437
  const resultSourceMap = options.resultSourceMap ?? config.resultSourceMap
405
438
  if (resultSourceMap !== undefined && resultSourceMap !== false) {
406
439
  options.query = {resultSourceMap, ...options.query}
@@ -482,6 +515,11 @@ export function _getDataUrl(
482
515
  path?: string,
483
516
  ): string {
484
517
  const config = client.config()
518
+ if (config['~experimental_resource']) {
519
+ const resourceBase = validators.resourceBase(config)
520
+ const uri = path ? `${operation}/${path}` : operation
521
+ return `${resourceBase}/${uri}`.replace(/\/($|\?)/, '$1')
522
+ }
485
523
  const catalog = validators.hasDataset(config)
486
524
  const baseUri = `/${operation}/${catalog}`
487
525
  const uri = path ? `${baseUri}/${path}` : baseUri
package/src/data/live.ts CHANGED
@@ -12,6 +12,7 @@ import type {
12
12
  SyncTag,
13
13
  } from '../types'
14
14
  import {shareReplayLatest} from '../util/shareReplayLatest'
15
+ import * as validate from '../validators'
15
16
  import {_getDataUrl} from './dataMethods'
16
17
  import {connectEventSource} from './eventsource'
17
18
  import {eventSourcePolyfill} from './eventsourcePolyfill'
@@ -43,6 +44,7 @@ export class LiveClient {
43
44
  */
44
45
  tag?: string
45
46
  } = {}): Observable<LiveEvent> {
47
+ validate.resourceGuard('live', this.#client.config())
46
48
  const {
47
49
  projectId,
48
50
  apiVersion: _apiVersion,
@@ -70,6 +70,7 @@ export class DatasetsClient {
70
70
  * @param options - Options for the dataset
71
71
  */
72
72
  create(name: string, options?: {aclMode?: DatasetAclMode}): Promise<DatasetResponse> {
73
+ validate.resourceGuard('dataset', this.#client.config())
73
74
  return lastValueFrom(
74
75
  _modify<DatasetResponse>(this.#client, this.#httpRequest, 'PUT', name, options),
75
76
  )
@@ -82,6 +83,7 @@ export class DatasetsClient {
82
83
  * @param options - New options for the dataset
83
84
  */
84
85
  edit(name: string, options?: {aclMode?: DatasetAclMode}): Promise<DatasetResponse> {
86
+ validate.resourceGuard('dataset', this.#client.config())
85
87
  return lastValueFrom(
86
88
  _modify<DatasetResponse>(this.#client, this.#httpRequest, 'PATCH', name, options),
87
89
  )
@@ -93,6 +95,7 @@ export class DatasetsClient {
93
95
  * @param name - Name of the dataset to delete
94
96
  */
95
97
  delete(name: string): Promise<{deleted: true}> {
98
+ validate.resourceGuard('dataset', this.#client.config())
96
99
  return lastValueFrom(_modify<{deleted: true}>(this.#client, this.#httpRequest, 'DELETE', name))
97
100
  }
98
101
 
@@ -100,6 +103,7 @@ export class DatasetsClient {
100
103
  * Fetch a list of datasets for the configured project
101
104
  */
102
105
  list(): Promise<DatasetsResponse> {
106
+ validate.resourceGuard('dataset', this.#client.config())
103
107
  return lastValueFrom(
104
108
  _request<DatasetsResponse>(this.#client, this.#httpRequest, {uri: '/datasets', tag: null}),
105
109
  )
@@ -113,6 +117,7 @@ function _modify<R = unknown>(
113
117
  name: string,
114
118
  options?: {aclMode?: DatasetAclMode},
115
119
  ) {
120
+ validate.resourceGuard('dataset', client.config())
116
121
  validate.dataset(name)
117
122
  return _request<R>(client, httpRequest, {
118
123
  method,
@@ -3,6 +3,7 @@ import {lastValueFrom, type Observable} from 'rxjs'
3
3
  import {_request} from '../data/dataMethods'
4
4
  import type {ObservableSanityClient, SanityClient} from '../SanityClient'
5
5
  import type {HttpRequest, SanityProject} from '../types'
6
+ import * as validate from '../validators'
6
7
 
7
8
  /** @internal */
8
9
  export class ObservableProjectsClient {
@@ -24,6 +25,7 @@ export class ObservableProjectsClient {
24
25
  list(options?: {
25
26
  includeMembers?: boolean
26
27
  }): Observable<SanityProject[] | Omit<SanityProject, 'members'>[]> {
28
+ validate.resourceGuard('projects', this.#client.config())
27
29
  const uri = options?.includeMembers === false ? '/projects?includeMembers=false' : '/projects'
28
30
  return _request<SanityProject[]>(this.#client, this.#httpRequest, {uri})
29
31
  }
@@ -34,6 +36,7 @@ export class ObservableProjectsClient {
34
36
  * @param projectId - ID of the project to fetch
35
37
  */
36
38
  getById(projectId: string): Observable<SanityProject> {
39
+ validate.resourceGuard('projects', this.#client.config())
37
40
  return _request<SanityProject>(this.#client, this.#httpRequest, {uri: `/projects/${projectId}`})
38
41
  }
39
42
  }
@@ -56,6 +59,7 @@ export class ProjectsClient {
56
59
  list(options?: {includeMembers?: true}): Promise<SanityProject[]>
57
60
  list(options?: {includeMembers?: false}): Promise<Omit<SanityProject, 'members'>[]>
58
61
  list(options?: {includeMembers?: boolean}): Promise<SanityProject[]> {
62
+ validate.resourceGuard('projects', this.#client.config())
59
63
  const uri = options?.includeMembers === false ? '/projects?includeMembers=false' : '/projects'
60
64
  return lastValueFrom(_request<SanityProject[]>(this.#client, this.#httpRequest, {uri}))
61
65
  }
@@ -66,6 +70,7 @@ export class ProjectsClient {
66
70
  * @param projectId - ID of the project to fetch
67
71
  */
68
72
  getById(projectId: string): Promise<SanityProject> {
73
+ validate.resourceGuard('projects', this.#client.config())
69
74
  return lastValueFrom(
70
75
  _request<SanityProject>(this.#client, this.#httpRequest, {uri: `/projects/${projectId}`}),
71
76
  )
package/src/types.ts CHANGED
@@ -53,6 +53,11 @@ export type ClientPerspective =
53
53
  | 'raw'
54
54
  | StackablePerspective[]
55
55
 
56
+ type ClientConfigResource = {
57
+ type: string
58
+ id: string
59
+ }
60
+
56
61
  /** @public */
57
62
  export interface ClientConfig {
58
63
  projectId?: string
@@ -61,6 +66,9 @@ export interface ClientConfig {
61
66
  useCdn?: boolean
62
67
  token?: string
63
68
 
69
+ /** @internal */
70
+ '~experimental_resource'?: ClientConfigResource
71
+
64
72
  /**
65
73
  * What perspective to use for the client. See {@link https://www.sanity.io/docs/perspectives|perspective documentation}
66
74
  * @remarks
@@ -1336,16 +1344,6 @@ export type ClientReturn<
1336
1344
  Fallback = Any,
1337
1345
  > = GroqString extends keyof SanityQueries ? SanityQueries[GroqString] : Fallback
1338
1346
 
1339
- export type {
1340
- InstructAsyncInstruction,
1341
- InstructConstantInstructionParam,
1342
- InstructFieldInstructionParam,
1343
- InstructGroqInstructionParam,
1344
- InstructInstruction,
1345
- InstructInstructionParam,
1346
- InstructInstructionParams,
1347
- InstructSyncInstruction,
1348
- } from './ai/types'
1349
1347
  export type {
1350
1348
  ContentSourceMapParsedPath,
1351
1349
  ContentSourceMapParsedPathKeyedSegment,
package/src/validators.ts CHANGED
@@ -76,3 +76,26 @@ export const requestTag = (tag: string) => {
76
76
 
77
77
  return tag
78
78
  }
79
+
80
+ export const resourceBase = (config: InitializedClientConfig): string => {
81
+ if (!config['~experimental_resource']) {
82
+ throw new Error('`resource` must be provided to perform queries')
83
+ }
84
+ const resourceConfig = config['~experimental_resource']
85
+
86
+ if (resourceConfig.type === 'dataset') {
87
+ const segments = resourceConfig.id.split('.')
88
+ if (segments.length !== 2) {
89
+ throw new Error('Dataset ID must be in the format "project.dataset"')
90
+ }
91
+ return `/projects/${segments[0]}/datasets/${segments[1]}`
92
+ }
93
+
94
+ return `/${resourceConfig.type}/${resourceConfig.id}`
95
+ }
96
+
97
+ export const resourceGuard = (service: string, config: InitializedClientConfig): void => {
98
+ if (config['~experimental_resource']) {
99
+ throw new Error(`\`${service}\` does not support resource-based operations`)
100
+ }
101
+ }
@@ -2015,8 +2015,8 @@
2015
2015
  }
2016
2016
  function shouldRetry(err, attempt, options) {
2017
2017
  if (options.maxRetries === 0) return false;
2018
- const isSafe = options.method === "GET" || options.method === "HEAD", isQuery = (options.uri || options.url).startsWith("/data/query"), isRetriableResponse = err.response && (err.response.statusCode === 429 || err.response.statusCode === 502 || err.response.statusCode === 503);
2019
- return (isSafe || isQuery) && isRetriableResponse ? true : P.shouldRetry(err, attempt, options);
2018
+ const isSafe = options.method === "GET" || options.method === "HEAD", isQuery2 = (options.uri || options.url).startsWith("/data/query"), isRetriableResponse = err.response && (err.response.statusCode === 429 || err.response.statusCode === 502 || err.response.statusCode === 503);
2019
+ return (isSafe || isQuery2) && isRetriableResponse ? true : P.shouldRetry(err, attempt, options);
2020
2020
  }
2021
2021
  const BASE_URL = "https://www.sanity.io/help/";
2022
2022
  function generateHelpUrl(slug) {
@@ -2063,6 +2063,20 @@
2063
2063
  "Tag can only contain alphanumeric characters, underscores, dashes and dots, and be between one and 75 characters long."
2064
2064
  );
2065
2065
  return tag;
2066
+ }, resourceBase = (config) => {
2067
+ if (!config["~experimental_resource"])
2068
+ throw new Error("`resource` must be provided to perform queries");
2069
+ const resourceConfig = config["~experimental_resource"];
2070
+ if (resourceConfig.type === "dataset") {
2071
+ const segments = resourceConfig.id.split(".");
2072
+ if (segments.length !== 2)
2073
+ throw new Error('Dataset ID must be in the format "project.dataset"');
2074
+ return `/projects/${segments[0]}/datasets/${segments[1]}`;
2075
+ }
2076
+ return `/${resourceConfig.type}/${resourceConfig.id}`;
2077
+ }, resourceGuard = (service, config) => {
2078
+ if (config["~experimental_resource"])
2079
+ throw new Error(`\`${service}\` does not support resource-based operations`);
2066
2080
  };
2067
2081
  function once(fn) {
2068
2082
  let didCall = false, returnValue;
@@ -2128,14 +2142,14 @@
2128
2142
  const newConfig = {
2129
2143
  ...defaultConfig,
2130
2144
  ...specifiedConfig
2131
- }, projectBased = newConfig.useProjectHostname;
2145
+ }, projectBased = newConfig.useProjectHostname && !newConfig["~experimental_resource"];
2132
2146
  if (typeof Promise > "u") {
2133
2147
  const helpUrl = generateHelpUrl("js-client-promise-polyfill");
2134
2148
  throw new Error(`No native Promise-implementation found, polyfill needed - see ${helpUrl}`);
2135
2149
  }
2136
2150
  if (projectBased && !newConfig.projectId)
2137
2151
  throw new Error("Configuration must contain `projectId`");
2138
- if (typeof newConfig.perspective < "u" && validateApiPerspective(newConfig.perspective), "encodeSourceMap" in newConfig)
2152
+ if (newConfig["~experimental_resource"] && resourceBase(newConfig), typeof newConfig.perspective < "u" && validateApiPerspective(newConfig.perspective), "encodeSourceMap" in newConfig)
2139
2153
  throw new Error(
2140
2154
  "It looks like you're using options meant for '@sanity/preview-kit/client'. 'encodeSourceMap' is not supported in '@sanity/client'. Did you mean 'stega.enabled'?"
2141
2155
  );
@@ -2154,7 +2168,7 @@
2154
2168
  const isBrowser = typeof window < "u" && window.location && window.location.hostname, isLocalhost = isBrowser && isLocal(window.location.hostname), hasToken = !!newConfig.token;
2155
2169
  newConfig.withCredentials && hasToken && (printCredentialedTokenWarning(), newConfig.withCredentials = false), isBrowser && isLocalhost && hasToken && newConfig.ignoreBrowserTokenWarning !== true ? printBrowserTokenWarning() : typeof newConfig.useCdn > "u" && printCdnWarning(), projectBased && projectId(newConfig.projectId), newConfig.dataset && dataset(newConfig.dataset), "requestTagPrefix" in newConfig && (newConfig.requestTagPrefix = newConfig.requestTagPrefix ? requestTag(newConfig.requestTagPrefix).replace(/\.+$/, "") : void 0), newConfig.apiVersion = `${newConfig.apiVersion}`.replace(/^v/, ""), newConfig.isDefaultApi = newConfig.apiHost === defaultConfig.apiHost, newConfig.useCdn === true && newConfig.withCredentials && printCdnAndWithCredentialsWarning(), newConfig.useCdn = newConfig.useCdn !== false && !newConfig.withCredentials, validateApiVersion(newConfig.apiVersion);
2156
2170
  const hostParts = newConfig.apiHost.split("://", 2), protocol = hostParts[0], host = hostParts[1], cdnHost = newConfig.isDefaultApi ? defaultCdnHost : host;
2157
- return newConfig.useProjectHostname ? (newConfig.url = `${protocol}://${newConfig.projectId}.${host}/v${newConfig.apiVersion}`, newConfig.cdnUrl = `${protocol}://${newConfig.projectId}.${cdnHost}/v${newConfig.apiVersion}`) : (newConfig.url = `${newConfig.apiHost}/v${newConfig.apiVersion}`, newConfig.cdnUrl = newConfig.url), newConfig;
2171
+ return projectBased ? (newConfig.url = `${protocol}://${newConfig.projectId}.${host}/v${newConfig.apiVersion}`, newConfig.cdnUrl = `${protocol}://${newConfig.projectId}.${cdnHost}/v${newConfig.apiVersion}`) : (newConfig.url = `${newConfig.apiHost}/v${newConfig.apiVersion}`, newConfig.cdnUrl = newConfig.url), newConfig;
2158
2172
  };
2159
2173
  class ConnectionFailedError extends Error {
2160
2174
  name = "ConnectionFailedError";
@@ -2722,7 +2736,7 @@ ${selectionOpts}`);
2722
2736
  );
2723
2737
  }
2724
2738
  function _dataRequest(client, httpRequest, endpoint, body, options = {}) {
2725
- const isMutation = endpoint === "mutate", isAction = endpoint === "actions", isQuery = endpoint === "query", strQuery = isMutation || isAction ? "" : encodeQueryString(body), useGet = !isMutation && !isAction && strQuery.length < getQuerySizeLimit, stringQuery = useGet ? strQuery : "", returnFirst = options.returnFirst, { timeout, token, tag, headers, returnQuery, lastLiveEventId, cacheMode } = options, uri = _getDataUrl(client, endpoint, stringQuery), reqOptions = {
2739
+ const isMutation = endpoint === "mutate", isAction = endpoint === "actions", isQuery2 = endpoint === "query", strQuery = isMutation || isAction ? "" : encodeQueryString(body), useGet = !isMutation && !isAction && strQuery.length < getQuerySizeLimit, stringQuery = useGet ? strQuery : "", returnFirst = options.returnFirst, { timeout, token, tag, headers, returnQuery, lastLiveEventId, cacheMode } = options, uri = _getDataUrl(client, endpoint, stringQuery), reqOptions = {
2726
2740
  method: useGet ? "GET" : "POST",
2727
2741
  uri,
2728
2742
  json: true,
@@ -2737,7 +2751,7 @@ ${selectionOpts}`);
2737
2751
  resultSourceMap: options.resultSourceMap,
2738
2752
  lastLiveEventId: Array.isArray(lastLiveEventId) ? lastLiveEventId[0] : lastLiveEventId,
2739
2753
  cacheMode,
2740
- canUseCdn: isQuery,
2754
+ canUseCdn: isQuery2,
2741
2755
  signal: options.signal,
2742
2756
  fetch: options.fetch,
2743
2757
  useAbortSignal: options.useAbortSignal,
@@ -2765,11 +2779,21 @@ ${selectionOpts}`);
2765
2779
  const mutation = { [op]: doc }, opts = Object.assign({ returnFirst: true, returnDocuments: true }, options);
2766
2780
  return _dataRequest(client, httpRequest, "mutate", { mutations: [mutation] }, opts);
2767
2781
  }
2782
+ const resourceDatasetRegex = /^\/projects\/([^/]+)\/datasets\/([^/]+)\/([^/^?]+)\/?/, isResourceDataEndpoint = (config, uri, endpoint) => {
2783
+ if (!config["~experimental_resource"])
2784
+ return false;
2785
+ if (uri.startsWith(
2786
+ `/${config["~experimental_resource"].type}/${config["~experimental_resource"].id}/${endpoint}`
2787
+ ))
2788
+ return true;
2789
+ const match = uri.match(resourceDatasetRegex);
2790
+ return match ? match[3] === endpoint : false;
2791
+ }, isQuery = (config, uri) => uri.startsWith("/data/query/") || isResourceDataEndpoint(config, uri, "query"), isMutate = (config, uri) => uri.startsWith("/data/mutate/") || isResourceDataEndpoint(config, uri, "mutate"), isDoc = (config, uri) => uri.startsWith("/data/doc/") || isResourceDataEndpoint(config, uri, "doc"), isListener = (config, uri) => uri.startsWith("/data/listen/") || isResourceDataEndpoint(config, uri, "listen"), isHistory = (config, uri) => uri.startsWith("/data/history/") || isResourceDataEndpoint(config, uri, "history"), isData = (config, uri) => uri.startsWith("/data/") || isQuery(config, uri) || isMutate(config, uri) || isDoc(config, uri) || isListener(config, uri) || isHistory(config, uri);
2768
2792
  function _requestObservable(client, httpRequest, options) {
2769
- const uri = options.url || options.uri, config = client.config(), canUseCdn = typeof options.canUseCdn > "u" ? ["GET", "HEAD"].indexOf(options.method || "GET") >= 0 && uri.indexOf("/data/") === 0 : options.canUseCdn;
2793
+ const uri = options.url || options.uri, config = client.config(), canUseCdn = typeof options.canUseCdn > "u" ? ["GET", "HEAD"].indexOf(options.method || "GET") >= 0 && isData(config, uri) : options.canUseCdn;
2770
2794
  let useCdn = (options.useCdn ?? config.useCdn) && canUseCdn;
2771
2795
  const tag = options.tag && config.requestTagPrefix ? [config.requestTagPrefix, options.tag].join(".") : options.tag || config.requestTagPrefix;
2772
- if (tag && options.tag !== null && (options.query = { tag: requestTag(tag), ...options.query }), ["GET", "HEAD", "POST"].indexOf(options.method || "GET") >= 0 && uri.indexOf("/data/query/") === 0) {
2796
+ if (tag && options.tag !== null && (options.query = { tag: requestTag(tag), ...options.query }), ["GET", "HEAD", "POST"].indexOf(options.method || "GET") >= 0 && isQuery(config, uri)) {
2773
2797
  const resultSourceMap = options.resultSourceMap ?? config.resultSourceMap;
2774
2798
  resultSourceMap !== void 0 && resultSourceMap !== false && (options.query = { resultSourceMap, ...options.query });
2775
2799
  const perspectiveOption = options.perspective || config.perspective;
@@ -2796,7 +2820,12 @@ ${selectionOpts}`);
2796
2820
  );
2797
2821
  }
2798
2822
  function _getDataUrl(client, operation, path) {
2799
- const config = client.config(), catalog = hasDataset(config), baseUri = `/${operation}/${catalog}`;
2823
+ const config = client.config();
2824
+ if (config["~experimental_resource"]) {
2825
+ const resourceBase$1 = resourceBase(config), uri2 = path ? `${operation}/${path}` : operation;
2826
+ return `${resourceBase$1}/${uri2}`.replace(/\/($|\?)/, "$1");
2827
+ }
2828
+ const catalog = hasDataset(config), baseUri = `/${operation}/${catalog}`;
2800
2829
  return `/data${path ? `${baseUri}/${path}` : baseUri}`.replace(/\/($|\?)/, "$1");
2801
2830
  }
2802
2831
  function _getUrl(client, uri, canUseCdn = false) {
@@ -2823,42 +2852,6 @@ ${selectionOpts}`);
2823
2852
  const error = new Error(signal?.reason ?? "The operation was aborted.");
2824
2853
  return error.name = "AbortError", error;
2825
2854
  }
2826
- function _instruct(client, httpRequest, request) {
2827
- const dataset2 = hasDataset(client.config());
2828
- return _request(client, httpRequest, {
2829
- method: "POST",
2830
- uri: `/assist/tasks/instruct/${dataset2}`,
2831
- body: request
2832
- });
2833
- }
2834
- class ObservableAiClient {
2835
- #client;
2836
- #httpRequest;
2837
- constructor(client, httpRequest) {
2838
- this.#client = client, this.#httpRequest = httpRequest;
2839
- }
2840
- /**
2841
- * Run an ad-hoc instruction for a target document.
2842
- * @param request instruction request
2843
- */
2844
- instruct(request) {
2845
- return _instruct(this.#client, this.#httpRequest, request);
2846
- }
2847
- }
2848
- class AiClient {
2849
- #client;
2850
- #httpRequest;
2851
- constructor(client, httpRequest) {
2852
- this.#client = client, this.#httpRequest = httpRequest;
2853
- }
2854
- /**
2855
- * Run an ad-hoc instruction for a target document.
2856
- * @param request instruction request
2857
- */
2858
- instruct(request) {
2859
- return lastValueFrom(_instruct(this.#client, this.#httpRequest, request));
2860
- }
2861
- }
2862
2855
  class ObservableAssetsClient {
2863
2856
  #client;
2864
2857
  #httpRequest;
@@ -2891,7 +2884,7 @@ ${selectionOpts}`);
2891
2884
  validateAssetType(assetType);
2892
2885
  let meta = opts.extract || void 0;
2893
2886
  meta && !meta.length && (meta = ["none"]);
2894
- const dataset2 = hasDataset(client.config()), assetEndpoint = assetType === "image" ? "images" : "files", options = optionsFromFile(opts, body), { tag, label, title, description, creditLine, filename, source } = options, query = {
2887
+ const config = client.config(), options = optionsFromFile(opts, body), { tag, label, title, description, creditLine, filename, source } = options, query = {
2895
2888
  label,
2896
2889
  title,
2897
2890
  description,
@@ -2903,12 +2896,19 @@ ${selectionOpts}`);
2903
2896
  tag,
2904
2897
  method: "POST",
2905
2898
  timeout: options.timeout || 0,
2906
- uri: `/assets/${assetEndpoint}/${dataset2}`,
2899
+ uri: buildAssetUploadUrl(config, assetType),
2907
2900
  headers: options.contentType ? { "Content-Type": options.contentType } : {},
2908
2901
  query,
2909
2902
  body
2910
2903
  });
2911
2904
  }
2905
+ function buildAssetUploadUrl(config, assetType) {
2906
+ const assetTypeEndpoint = assetType === "image" ? "images" : "files";
2907
+ if (config["~experimental_resource"])
2908
+ return `${config["~experimental_resource"].type}/${config["~experimental_resource"].id}/assets/${assetTypeEndpoint}`;
2909
+ const dataset2 = hasDataset(config);
2910
+ return `assets/${assetTypeEndpoint}/${dataset2}`;
2911
+ }
2912
2912
  function optionsFromFile(opts, file) {
2913
2913
  return typeof File > "u" || !(file instanceof File) ? opts : Object.assign(
2914
2914
  {
@@ -3000,6 +3000,7 @@ ${selectionOpts}`);
3000
3000
  includeDrafts = false,
3001
3001
  tag: _tag
3002
3002
  } = {}) {
3003
+ resourceGuard("live", this.#client.config());
3003
3004
  const {
3004
3005
  projectId: projectId2,
3005
3006
  apiVersion: _apiVersion,
@@ -3129,7 +3130,7 @@ ${selectionOpts}`);
3129
3130
  * @param options - Options for the dataset
3130
3131
  */
3131
3132
  create(name, options) {
3132
- return lastValueFrom(
3133
+ return resourceGuard("dataset", this.#client.config()), lastValueFrom(
3133
3134
  _modify(this.#client, this.#httpRequest, "PUT", name, options)
3134
3135
  );
3135
3136
  }
@@ -3140,7 +3141,7 @@ ${selectionOpts}`);
3140
3141
  * @param options - New options for the dataset
3141
3142
  */
3142
3143
  edit(name, options) {
3143
- return lastValueFrom(
3144
+ return resourceGuard("dataset", this.#client.config()), lastValueFrom(
3144
3145
  _modify(this.#client, this.#httpRequest, "PATCH", name, options)
3145
3146
  );
3146
3147
  }
@@ -3150,19 +3151,19 @@ ${selectionOpts}`);
3150
3151
  * @param name - Name of the dataset to delete
3151
3152
  */
3152
3153
  delete(name) {
3153
- return lastValueFrom(_modify(this.#client, this.#httpRequest, "DELETE", name));
3154
+ return resourceGuard("dataset", this.#client.config()), lastValueFrom(_modify(this.#client, this.#httpRequest, "DELETE", name));
3154
3155
  }
3155
3156
  /**
3156
3157
  * Fetch a list of datasets for the configured project
3157
3158
  */
3158
3159
  list() {
3159
- return lastValueFrom(
3160
+ return resourceGuard("dataset", this.#client.config()), lastValueFrom(
3160
3161
  _request(this.#client, this.#httpRequest, { uri: "/datasets", tag: null })
3161
3162
  );
3162
3163
  }
3163
3164
  }
3164
3165
  function _modify(client, httpRequest, method, name, options) {
3165
- return dataset(name), _request(client, httpRequest, {
3166
+ return resourceGuard("dataset", client.config()), dataset(name), _request(client, httpRequest, {
3166
3167
  method,
3167
3168
  uri: `/datasets/${name}`,
3168
3169
  body: options,
@@ -3176,6 +3177,7 @@ ${selectionOpts}`);
3176
3177
  this.#client = client, this.#httpRequest = httpRequest;
3177
3178
  }
3178
3179
  list(options) {
3180
+ resourceGuard("projects", this.#client.config());
3179
3181
  const uri = options?.includeMembers === false ? "/projects?includeMembers=false" : "/projects";
3180
3182
  return _request(this.#client, this.#httpRequest, { uri });
3181
3183
  }
@@ -3185,7 +3187,7 @@ ${selectionOpts}`);
3185
3187
  * @param projectId - ID of the project to fetch
3186
3188
  */
3187
3189
  getById(projectId2) {
3188
- return _request(this.#client, this.#httpRequest, { uri: `/projects/${projectId2}` });
3190
+ return resourceGuard("projects", this.#client.config()), _request(this.#client, this.#httpRequest, { uri: `/projects/${projectId2}` });
3189
3191
  }
3190
3192
  }
3191
3193
  class ProjectsClient {
@@ -3195,6 +3197,7 @@ ${selectionOpts}`);
3195
3197
  this.#client = client, this.#httpRequest = httpRequest;
3196
3198
  }
3197
3199
  list(options) {
3200
+ resourceGuard("projects", this.#client.config());
3198
3201
  const uri = options?.includeMembers === false ? "/projects?includeMembers=false" : "/projects";
3199
3202
  return lastValueFrom(_request(this.#client, this.#httpRequest, { uri }));
3200
3203
  }
@@ -3204,7 +3207,7 @@ ${selectionOpts}`);
3204
3207
  * @param projectId - ID of the project to fetch
3205
3208
  */
3206
3209
  getById(projectId2) {
3207
- return lastValueFrom(
3210
+ return resourceGuard("projects", this.#client.config()), lastValueFrom(
3208
3211
  _request(this.#client, this.#httpRequest, { uri: `/projects/${projectId2}` })
3209
3212
  );
3210
3213
  }
@@ -3253,7 +3256,6 @@ ${selectionOpts}`);
3253
3256
  live;
3254
3257
  projects;
3255
3258
  users;
3256
- ai;
3257
3259
  /**
3258
3260
  * Private properties
3259
3261
  */
@@ -3264,7 +3266,7 @@ ${selectionOpts}`);
3264
3266
  */
3265
3267
  listen = _listen;
3266
3268
  constructor(httpRequest, config = defaultConfig) {
3267
- this.config(config), this.#httpRequest = httpRequest, this.assets = new ObservableAssetsClient(this, this.#httpRequest), this.datasets = new ObservableDatasetsClient(this, this.#httpRequest), this.live = new LiveClient(this), this.projects = new ObservableProjectsClient(this, this.#httpRequest), this.users = new ObservableUsersClient(this, this.#httpRequest), this.ai = new ObservableAiClient(this, this.#httpRequest);
3269
+ this.config(config), this.#httpRequest = httpRequest, this.assets = new ObservableAssetsClient(this, this.#httpRequest), this.datasets = new ObservableDatasetsClient(this, this.#httpRequest), this.live = new LiveClient(this), this.projects = new ObservableProjectsClient(this, this.#httpRequest), this.users = new ObservableUsersClient(this, this.#httpRequest);
3268
3270
  }
3269
3271
  /**
3270
3272
  * Clone the client - returns a new instance
@@ -3403,7 +3405,6 @@ ${selectionOpts}`);
3403
3405
  live;
3404
3406
  projects;
3405
3407
  users;
3406
- ai;
3407
3408
  /**
3408
3409
  * Observable version of the Sanity client, with the same configuration as the promise-based one
3409
3410
  */
@@ -3418,7 +3419,7 @@ ${selectionOpts}`);
3418
3419
  */
3419
3420
  listen = _listen;
3420
3421
  constructor(httpRequest, config = defaultConfig) {
3421
- this.config(config), this.#httpRequest = httpRequest, this.assets = new AssetsClient(this, this.#httpRequest), this.datasets = new DatasetsClient(this, this.#httpRequest), this.live = new LiveClient(this), this.projects = new ProjectsClient(this, this.#httpRequest), this.users = new UsersClient(this, this.#httpRequest), this.ai = new AiClient(this, this.#httpRequest), this.observable = new ObservableSanityClient(httpRequest, config);
3422
+ this.config(config), this.#httpRequest = httpRequest, this.assets = new AssetsClient(this, this.#httpRequest), this.datasets = new DatasetsClient(this, this.#httpRequest), this.live = new LiveClient(this), this.projects = new ProjectsClient(this, this.#httpRequest), this.users = new UsersClient(this, this.#httpRequest), this.observable = new ObservableSanityClient(httpRequest, config);
3422
3423
  }
3423
3424
  /**
3424
3425
  * Clone the client - returns a new instance