@sanity/client 6.28.4 → 6.28.5-goaway.0

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.
@@ -333,6 +333,8 @@ export declare interface ClientConfig {
333
333
  /** @defaultValue true */
334
334
  useCdn?: boolean
335
335
  token?: string
336
+ /** @internal */
337
+ '~experimental_resource'?: ClientConfigResource
336
338
  /**
337
339
  * What perspective to use for the client. See {@link https://www.sanity.io/docs/perspectives|perspective documentation}
338
340
  * @remarks
@@ -393,6 +395,24 @@ export declare interface ClientConfig {
393
395
  stega?: StegaConfig | boolean
394
396
  }
395
397
 
398
+ declare type ClientConfigResource =
399
+ | {
400
+ type: 'canvas'
401
+ id: string
402
+ }
403
+ | {
404
+ type: 'media-library'
405
+ id: string
406
+ }
407
+ | {
408
+ type: 'dataset'
409
+ id: string
410
+ }
411
+ | {
412
+ type: 'dashboard'
413
+ id: string
414
+ }
415
+
396
416
  /** @public */
397
417
  export declare class ClientError extends Error {
398
418
  response: ErrorProps['response']
@@ -1209,6 +1229,18 @@ export declare type LiveEvent =
1209
1229
  | LiveEventReconnect
1210
1230
  | LiveEventMessage
1211
1231
  | LiveEventWelcome
1232
+ | LiveEventGoAway
1233
+
1234
+ /**
1235
+ * The `id` field is the position at which the connection was rejected or closed.
1236
+ * The `reason` field will specify why the connection rejected/closed.
1237
+ * @public
1238
+ */
1239
+ export declare interface LiveEventGoAway {
1240
+ type: 'goaway'
1241
+ id: string
1242
+ reason: string
1243
+ }
1212
1244
 
1213
1245
  /** @public */
1214
1246
  export declare interface LiveEventMessage {
@@ -333,6 +333,8 @@ export declare interface ClientConfig {
333
333
  /** @defaultValue true */
334
334
  useCdn?: boolean
335
335
  token?: string
336
+ /** @internal */
337
+ '~experimental_resource'?: ClientConfigResource
336
338
  /**
337
339
  * What perspective to use for the client. See {@link https://www.sanity.io/docs/perspectives|perspective documentation}
338
340
  * @remarks
@@ -393,6 +395,24 @@ export declare interface ClientConfig {
393
395
  stega?: StegaConfig | boolean
394
396
  }
395
397
 
398
+ declare type ClientConfigResource =
399
+ | {
400
+ type: 'canvas'
401
+ id: string
402
+ }
403
+ | {
404
+ type: 'media-library'
405
+ id: string
406
+ }
407
+ | {
408
+ type: 'dataset'
409
+ id: string
410
+ }
411
+ | {
412
+ type: 'dashboard'
413
+ id: string
414
+ }
415
+
396
416
  /** @public */
397
417
  export declare class ClientError extends Error {
398
418
  response: ErrorProps['response']
@@ -1209,6 +1229,18 @@ export declare type LiveEvent =
1209
1229
  | LiveEventReconnect
1210
1230
  | LiveEventMessage
1211
1231
  | LiveEventWelcome
1232
+ | LiveEventGoAway
1233
+
1234
+ /**
1235
+ * The `id` field is the position at which the connection was rejected or closed.
1236
+ * The `reason` field will specify why the connection rejected/closed.
1237
+ * @public
1238
+ */
1239
+ export declare interface LiveEventGoAway {
1240
+ type: 'goaway'
1241
+ id: string
1242
+ reason: string
1243
+ }
1212
1244
 
1213
1245
  /** @public */
1214
1246
  export declare interface LiveEventMessage {
package/dist/stega.d.cts CHANGED
@@ -333,6 +333,8 @@ export declare interface ClientConfig {
333
333
  /** @defaultValue true */
334
334
  useCdn?: boolean
335
335
  token?: string
336
+ /** @internal */
337
+ '~experimental_resource'?: ClientConfigResource
336
338
  /**
337
339
  * What perspective to use for the client. See {@link https://www.sanity.io/docs/perspectives|perspective documentation}
338
340
  * @remarks
@@ -393,6 +395,24 @@ export declare interface ClientConfig {
393
395
  stega?: StegaConfig | boolean
394
396
  }
395
397
 
398
+ declare type ClientConfigResource =
399
+ | {
400
+ type: 'canvas'
401
+ id: string
402
+ }
403
+ | {
404
+ type: 'media-library'
405
+ id: string
406
+ }
407
+ | {
408
+ type: 'dataset'
409
+ id: string
410
+ }
411
+ | {
412
+ type: 'dashboard'
413
+ id: string
414
+ }
415
+
396
416
  /** @public */
397
417
  export declare class ClientError extends Error {
398
418
  response: ErrorProps['response']
@@ -1209,6 +1229,18 @@ export declare type LiveEvent =
1209
1229
  | LiveEventReconnect
1210
1230
  | LiveEventMessage
1211
1231
  | LiveEventWelcome
1232
+ | LiveEventGoAway
1233
+
1234
+ /**
1235
+ * The `id` field is the position at which the connection was rejected or closed.
1236
+ * The `reason` field will specify why the connection rejected/closed.
1237
+ * @public
1238
+ */
1239
+ export declare interface LiveEventGoAway {
1240
+ type: 'goaway'
1241
+ id: string
1242
+ reason: string
1243
+ }
1212
1244
 
1213
1245
  /** @public */
1214
1246
  export declare interface LiveEventMessage {
package/dist/stega.d.ts CHANGED
@@ -333,6 +333,8 @@ export declare interface ClientConfig {
333
333
  /** @defaultValue true */
334
334
  useCdn?: boolean
335
335
  token?: string
336
+ /** @internal */
337
+ '~experimental_resource'?: ClientConfigResource
336
338
  /**
337
339
  * What perspective to use for the client. See {@link https://www.sanity.io/docs/perspectives|perspective documentation}
338
340
  * @remarks
@@ -393,6 +395,24 @@ export declare interface ClientConfig {
393
395
  stega?: StegaConfig | boolean
394
396
  }
395
397
 
398
+ declare type ClientConfigResource =
399
+ | {
400
+ type: 'canvas'
401
+ id: string
402
+ }
403
+ | {
404
+ type: 'media-library'
405
+ id: string
406
+ }
407
+ | {
408
+ type: 'dataset'
409
+ id: string
410
+ }
411
+ | {
412
+ type: 'dashboard'
413
+ id: string
414
+ }
415
+
396
416
  /** @public */
397
417
  export declare class ClientError extends Error {
398
418
  response: ErrorProps['response']
@@ -1209,6 +1229,18 @@ export declare type LiveEvent =
1209
1229
  | LiveEventReconnect
1210
1230
  | LiveEventMessage
1211
1231
  | LiveEventWelcome
1232
+ | LiveEventGoAway
1233
+
1234
+ /**
1235
+ * The `id` field is the position at which the connection was rejected or closed.
1236
+ * The `reason` field will specify why the connection rejected/closed.
1237
+ * @public
1238
+ */
1239
+ export declare interface LiveEventGoAway {
1240
+ type: 'goaway'
1241
+ id: string
1242
+ reason: string
1243
+ }
1212
1244
 
1213
1245
  /** @public */
1214
1246
  export declare interface LiveEventMessage {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/client",
3
- "version": "6.28.4",
3
+ "version": "6.28.5-goaway.0",
4
4
  "description": "Client for retrieving, creating and patching data from Sanity.io",
5
5
  "keywords": [
6
6
  "sanity",
@@ -125,41 +125,41 @@
125
125
  "devDependencies": {
126
126
  "@edge-runtime/types": "^4.0.0",
127
127
  "@edge-runtime/vm": "^5.0.0",
128
- "@eslint/eslintrc": "^3.3.0",
129
- "@eslint/js": "^9.22.0",
128
+ "@eslint/eslintrc": "^3.3.1",
129
+ "@eslint/js": "^9.24.0",
130
130
  "@rollup/plugin-commonjs": "^28.0.3",
131
131
  "@rollup/plugin-node-resolve": "^16.0.1",
132
132
  "@sanity/client-latest": "npm:@sanity/client@latest",
133
- "@sanity/pkg-utils": "^7.1.1",
133
+ "@sanity/pkg-utils": "^7.2.2",
134
134
  "@types/json-diff": "^1.0.3",
135
135
  "@types/node": "^22.9.0",
136
- "@typescript-eslint/eslint-plugin": "^8.26.1",
137
- "@typescript-eslint/parser": "^8.26.1",
136
+ "@typescript-eslint/eslint-plugin": "^8.29.1",
137
+ "@typescript-eslint/parser": "^8.29.1",
138
138
  "@vercel/stega": "0.1.2",
139
- "@vitest/coverage-v8": "3.0.9",
140
- "eslint": "^9.22.0",
139
+ "@vitest/coverage-v8": "3.1.1",
140
+ "eslint": "^9.24.0",
141
141
  "eslint-config-prettier": "^10.1.1",
142
142
  "eslint-formatter-compact": "^8.40.0",
143
- "eslint-plugin-prettier": "^5.2.3",
143
+ "eslint-plugin-prettier": "^5.2.6",
144
144
  "eslint-plugin-simple-import-sort": "^12.1.1",
145
145
  "faucet": "^0.0.4",
146
- "globals": "^15.15.0",
146
+ "globals": "^16.0.0",
147
147
  "happy-dom": "^12.10.3",
148
148
  "json-diff": "^1.0.6",
149
149
  "ls-engines": "^0.9.3",
150
150
  "msw": "^2.7.3",
151
- "next": "^15.2.3",
151
+ "next": "^15.3.0",
152
152
  "nock": "^13.5.6",
153
153
  "prettier": "^3.5.3",
154
154
  "prettier-plugin-packagejson": "^2.5.10",
155
155
  "rimraf": "^5.0.7",
156
- "rollup": "^4.36.0",
156
+ "rollup": "^4.39.0",
157
157
  "sse-channel": "^4.0.0",
158
158
  "terser": "^5.39.0",
159
- "typescript": "5.8.2",
160
- "vitest": "3.0.9"
159
+ "typescript": "5.8.3",
160
+ "vitest": "3.1.1"
161
161
  },
162
- "packageManager": "npm@11.0.0",
162
+ "packageManager": "npm@11.3.0",
163
163
  "engines": {
164
164
  "node": ">=14.18"
165
165
  }
@@ -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,48 @@ 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
+ const {type, id} = config['~experimental_resource']
186
+ switch (type) {
187
+ case 'dataset': {
188
+ throw new Error(
189
+ 'Assets are not supported for dataset resources, yet. Configure the client with `{projectId: <projectId>, dataset: <datasetId>}` instead.',
190
+ )
191
+ }
192
+ case 'canvas': {
193
+ return `/canvases/${id}/assets/${assetTypeEndpoint}`
194
+ }
195
+ case 'media-library': {
196
+ return `/media-libraries/${id}/upload`
197
+ }
198
+ case 'dashboard': {
199
+ return `/dashboards/${id}/assets/${assetTypeEndpoint}`
200
+ }
201
+ default:
202
+ // @ts-expect-error - handle all supported resource types
203
+ throw new Error(`Unsupported resource type: ${type.toString()}`)
204
+ }
205
+ }
206
+
207
+ const dataset = validators.hasDataset(config)
208
+ return `assets/${assetTypeEndpoint}/${dataset}`
209
+ }
210
+
180
211
  function optionsFromFile(opts: Record<string, Any>, file: Any) {
181
212
  if (typeof File === 'undefined' || !(file instanceof File)) {
182
213
  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.resourceConfig(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 {
@@ -17,6 +17,7 @@ import type {
17
17
  HttpRequest,
18
18
  HttpRequestEvent,
19
19
  IdentifiedSanityDocumentStub,
20
+ InitializedClientConfig,
20
21
  InitializedStegaConfig,
21
22
  MultipleActionResult,
22
23
  MultipleMutationResult,
@@ -38,6 +39,8 @@ import {encodeQueryString} from './encodeQueryString'
38
39
  import {ObservablePatch, Patch} from './patch'
39
40
  import {ObservableTransaction, Transaction} from './transaction'
40
41
 
42
+ type Client = SanityClient | ObservableSanityClient
43
+
41
44
  const excludeFalsey = (param: Any, defValue: Any) => {
42
45
  const value = typeof param === 'undefined' ? defValue : param
43
46
  return param === false ? undefined : value
@@ -67,7 +70,7 @@ const getQuerySizeLimit = 11264
67
70
 
68
71
  /** @internal */
69
72
  export function _fetch<R, Q>(
70
- client: ObservableSanityClient | SanityClient,
73
+ client: Client,
71
74
  httpRequest: HttpRequest,
72
75
  _stega: InitializedStegaConfig,
73
76
  query: string,
@@ -126,7 +129,7 @@ export function _fetch<R, Q>(
126
129
 
127
130
  /** @internal */
128
131
  export function _getDocument<R extends Record<string, Any>>(
129
- client: ObservableSanityClient | SanityClient,
132
+ client: Client,
130
133
  httpRequest: HttpRequest,
131
134
  id: string,
132
135
  opts: {signal?: AbortSignal; tag?: string} = {},
@@ -145,7 +148,7 @@ export function _getDocument<R extends Record<string, Any>>(
145
148
 
146
149
  /** @internal */
147
150
  export function _getDocuments<R extends Record<string, Any>>(
148
- client: ObservableSanityClient | SanityClient,
151
+ client: Client,
149
152
  httpRequest: HttpRequest,
150
153
  ids: string[],
151
154
  opts: {signal?: AbortSignal; tag?: string} = {},
@@ -167,7 +170,7 @@ export function _getDocuments<R extends Record<string, Any>>(
167
170
 
168
171
  /** @internal */
169
172
  export function _createIfNotExists<R extends Record<string, Any>>(
170
- client: ObservableSanityClient | SanityClient,
173
+ client: Client,
171
174
  httpRequest: HttpRequest,
172
175
  doc: IdentifiedSanityDocumentStub<R>,
173
176
  options?:
@@ -185,7 +188,7 @@ export function _createIfNotExists<R extends Record<string, Any>>(
185
188
 
186
189
  /** @internal */
187
190
  export function _createOrReplace<R extends Record<string, Any>>(
188
- client: ObservableSanityClient | SanityClient,
191
+ client: Client,
189
192
  httpRequest: HttpRequest,
190
193
  doc: IdentifiedSanityDocumentStub<R>,
191
194
  options?:
@@ -203,7 +206,7 @@ export function _createOrReplace<R extends Record<string, Any>>(
203
206
 
204
207
  /** @internal */
205
208
  export function _delete<R extends Record<string, Any>>(
206
- client: ObservableSanityClient | SanityClient,
209
+ client: Client,
207
210
  httpRequest: HttpRequest,
208
211
  selection: string | MutationSelection,
209
212
  options?:
@@ -226,7 +229,7 @@ export function _delete<R extends Record<string, Any>>(
226
229
 
227
230
  /** @internal */
228
231
  export function _mutate<R extends Record<string, Any>>(
229
- client: SanityClient | ObservableSanityClient,
232
+ client: Client,
230
233
  httpRequest: HttpRequest,
231
234
  mutations: Mutation<R>[] | Patch | ObservablePatch | Transaction | ObservableTransaction,
232
235
  options?:
@@ -256,7 +259,7 @@ export function _mutate<R extends Record<string, Any>>(
256
259
  * @internal
257
260
  */
258
261
  export function _action(
259
- client: SanityClient | ObservableSanityClient,
262
+ client: Client,
260
263
  httpRequest: HttpRequest,
261
264
  actions: Action | Action[],
262
265
  options?: BaseActionOptions,
@@ -280,7 +283,7 @@ export function _action(
280
283
  * @internal
281
284
  */
282
285
  export function _dataRequest(
283
- client: SanityClient | ObservableSanityClient,
286
+ client: Client,
284
287
  httpRequest: HttpRequest,
285
288
  endpoint: string,
286
289
  body: Any,
@@ -354,7 +357,7 @@ export function _dataRequest(
354
357
  * @internal
355
358
  */
356
359
  export function _create<R extends Record<string, Any>>(
357
- client: SanityClient | ObservableSanityClient,
360
+ client: Client,
358
361
  httpRequest: HttpRequest,
359
362
  doc: Any,
360
363
  op: Any,
@@ -367,11 +370,38 @@ export function _create<R extends Record<string, Any>>(
367
370
  return _dataRequest(client, httpRequest, 'mutate', {mutations: [mutation]}, opts)
368
371
  }
369
372
 
373
+ const hasDataConfig = (client: Client) =>
374
+ (client.config().dataset !== undefined && client.config().projectId !== undefined) ||
375
+ client.config()['~experimental_resource'] !== undefined
376
+
377
+ const isQuery = (client: Client, uri: string) =>
378
+ hasDataConfig(client) && uri.startsWith(_getDataUrl(client, 'query'))
379
+
380
+ const isMutate = (client: Client, uri: string) =>
381
+ hasDataConfig(client) && uri.startsWith(_getDataUrl(client, 'mutate'))
382
+
383
+ const isDoc = (client: Client, uri: string) =>
384
+ hasDataConfig(client) && uri.startsWith(_getDataUrl(client, 'doc', ''))
385
+
386
+ const isListener = (client: Client, uri: string) =>
387
+ hasDataConfig(client) && uri.startsWith(_getDataUrl(client, 'listen'))
388
+
389
+ const isHistory = (client: Client, uri: string) =>
390
+ hasDataConfig(client) && uri.startsWith(_getDataUrl(client, 'history', ''))
391
+
392
+ const isData = (client: Client, uri: string) =>
393
+ uri.startsWith('/data/') ||
394
+ isQuery(client, uri) ||
395
+ isMutate(client, uri) ||
396
+ isDoc(client, uri) ||
397
+ isListener(client, uri) ||
398
+ isHistory(client, uri)
399
+
370
400
  /**
371
401
  * @internal
372
402
  */
373
403
  export function _requestObservable<R>(
374
- client: SanityClient | ObservableSanityClient,
404
+ client: Client,
375
405
  httpRequest: HttpRequest,
376
406
  options: RequestObservableOptions,
377
407
  ): Observable<HttpRequestEvent<R>> {
@@ -382,7 +412,7 @@ export function _requestObservable<R>(
382
412
  // Only the /data endpoint is currently available through API-CDN.
383
413
  const canUseCdn =
384
414
  typeof options.canUseCdn === 'undefined'
385
- ? ['GET', 'HEAD'].indexOf(options.method || 'GET') >= 0 && uri.indexOf('/data/') === 0
415
+ ? ['GET', 'HEAD'].indexOf(options.method || 'GET') >= 0 && isData(client, uri)
386
416
  : options.canUseCdn
387
417
 
388
418
  let useCdn = (options.useCdn ?? config.useCdn) && canUseCdn
@@ -397,10 +427,7 @@ export function _requestObservable<R>(
397
427
  }
398
428
 
399
429
  // GROQ query-only parameters
400
- if (
401
- ['GET', 'HEAD', 'POST'].indexOf(options.method || 'GET') >= 0 &&
402
- uri.indexOf('/data/query/') === 0
403
- ) {
430
+ if (['GET', 'HEAD', 'POST'].indexOf(options.method || 'GET') >= 0 && isQuery(client, uri)) {
404
431
  const resultSourceMap = options.resultSourceMap ?? config.resultSourceMap
405
432
  if (resultSourceMap !== undefined && resultSourceMap !== false) {
406
433
  options.query = {resultSourceMap, ...options.query}
@@ -460,11 +487,7 @@ export function _requestObservable<R>(
460
487
  /**
461
488
  * @internal
462
489
  */
463
- export function _request<R>(
464
- client: SanityClient | ObservableSanityClient,
465
- httpRequest: HttpRequest,
466
- options: Any,
467
- ): Observable<R> {
490
+ export function _request<R>(client: Client, httpRequest: HttpRequest, options: Any): Observable<R> {
468
491
  const observable = _requestObservable<R>(client, httpRequest, options).pipe(
469
492
  filter((event: Any) => event.type === 'response'),
470
493
  map((event: Any) => event.body),
@@ -476,26 +499,24 @@ export function _request<R>(
476
499
  /**
477
500
  * @internal
478
501
  */
479
- export function _getDataUrl(
480
- client: SanityClient | ObservableSanityClient,
481
- operation: string,
482
- path?: string,
483
- ): string {
502
+ export function _getDataUrl(client: Client, operation: string, path?: string): string {
484
503
  const config = client.config()
504
+ if (config['~experimental_resource']) {
505
+ validators.resourceConfig(config)
506
+ const resourceBase = resourceDataBase(config)
507
+ const uri = path !== undefined ? `${operation}/${path}` : operation
508
+ return `${resourceBase}/${uri}`.replace(/\/($|\?)/, '$1')
509
+ }
485
510
  const catalog = validators.hasDataset(config)
486
511
  const baseUri = `/${operation}/${catalog}`
487
- const uri = path ? `${baseUri}/${path}` : baseUri
512
+ const uri = path !== undefined ? `${baseUri}/${path}` : baseUri
488
513
  return `/data${uri}`.replace(/\/($|\?)/, '$1')
489
514
  }
490
515
 
491
516
  /**
492
517
  * @internal
493
518
  */
494
- export function _getUrl(
495
- client: SanityClient | ObservableSanityClient,
496
- uri: string,
497
- canUseCdn = false,
498
- ): string {
519
+ export function _getUrl(client: Client, uri: string, canUseCdn = false): string {
499
520
  const {url, cdnUrl} = client.config()
500
521
  const base = canUseCdn ? cdnUrl : url
501
522
  return `${base}/${uri.replace(/^\//, '')}`
@@ -547,3 +568,32 @@ function _createAbortError(signal?: AbortSignal) {
547
568
 
548
569
  return error
549
570
  }
571
+
572
+ const resourceDataBase = (config: InitializedClientConfig): string => {
573
+ if (!config['~experimental_resource']) {
574
+ throw new Error('`resource` must be provided to perform resource queries')
575
+ }
576
+ const {type, id} = config['~experimental_resource']
577
+
578
+ switch (type) {
579
+ case 'dataset': {
580
+ const segments = id.split('.')
581
+ if (segments.length !== 2) {
582
+ throw new Error('Dataset ID must be in the format "project.dataset"')
583
+ }
584
+ return `/projects/${segments[0]}/datasets/${segments[1]}`
585
+ }
586
+ case 'canvas': {
587
+ return `/canvases/${id}`
588
+ }
589
+ case 'media-library': {
590
+ return `/media-libraries/${id}`
591
+ }
592
+ case 'dashboard': {
593
+ return `/dashboards/${id}`
594
+ }
595
+ default:
596
+ // @ts-expect-error - handle all supported resource types
597
+ throw new Error(`Unsupported resource type: ${type.toString()}`)
598
+ }
599
+ }
package/src/data/live.ts CHANGED
@@ -5,6 +5,7 @@ import {CorsOriginError} from '../http/errors'
5
5
  import type {ObservableSanityClient, SanityClient} from '../SanityClient'
6
6
  import type {
7
7
  LiveEvent,
8
+ LiveEventGoAway,
8
9
  LiveEventMessage,
9
10
  LiveEventReconnect,
10
11
  LiveEventRestart,
@@ -12,6 +13,7 @@ import type {
12
13
  SyncTag,
13
14
  } from '../types'
14
15
  import {shareReplayLatest} from '../util/shareReplayLatest'
16
+ import * as validate from '../validators'
15
17
  import {_getDataUrl} from './dataMethods'
16
18
  import {connectEventSource} from './eventsource'
17
19
  import {eventSourcePolyfill} from './eventsourcePolyfill'
@@ -43,6 +45,7 @@ export class LiveClient {
43
45
  */
44
46
  tag?: string
45
47
  } = {}): Observable<LiveEvent> {
48
+ validate.resourceGuard('live', this.#client.config())
46
49
  const {
47
50
  projectId,
48
51
  apiVersion: _apiVersion,
@@ -101,6 +104,7 @@ export class LiveClient {
101
104
  'restart',
102
105
  'welcome',
103
106
  'reconnect',
107
+ 'goaway',
104
108
  ]).pipe(
105
109
  reconnectOnConnectionFailure(),
106
110
  map((event) => {
@@ -109,7 +113,7 @@ export class LiveClient {
109
113
  // Splat data properties from the eventsource message onto the returned event
110
114
  return {...rest, tags: (data as {tags: SyncTag[]}).tags} as LiveEventMessage
111
115
  }
112
- return event as LiveEventRestart | LiveEventReconnect | LiveEventWelcome
116
+ return event as LiveEventRestart | LiveEventReconnect | LiveEventWelcome | LiveEventGoAway
113
117
  }),
114
118
  )
115
119