@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.
@@ -339,6 +339,8 @@ export declare interface ClientConfig {
339
339
  /** @defaultValue true */
340
340
  useCdn?: boolean
341
341
  token?: string
342
+ /** @internal */
343
+ '~experimental_resource'?: ClientConfigResource
342
344
  /**
343
345
  * What perspective to use for the client. See {@link https://www.sanity.io/docs/perspectives|perspective documentation}
344
346
  * @remarks
@@ -399,6 +401,24 @@ export declare interface ClientConfig {
399
401
  stega?: StegaConfig | boolean
400
402
  }
401
403
 
404
+ declare type ClientConfigResource =
405
+ | {
406
+ type: 'canvas'
407
+ id: string
408
+ }
409
+ | {
410
+ type: 'media-library'
411
+ id: string
412
+ }
413
+ | {
414
+ type: 'dataset'
415
+ id: string
416
+ }
417
+ | {
418
+ type: 'dashboard'
419
+ id: string
420
+ }
421
+
402
422
  /** @public */
403
423
  export declare class ClientError extends Error {
404
424
  response: ErrorProps['response']
@@ -1039,6 +1059,18 @@ export declare type LiveEvent =
1039
1059
  | LiveEventReconnect
1040
1060
  | LiveEventMessage
1041
1061
  | LiveEventWelcome
1062
+ | LiveEventGoAway
1063
+
1064
+ /**
1065
+ * The `id` field is the position at which the connection was rejected or closed.
1066
+ * The `reason` field will specify why the connection rejected/closed.
1067
+ * @public
1068
+ */
1069
+ export declare interface LiveEventGoAway {
1070
+ type: 'goaway'
1071
+ id: string
1072
+ reason: string
1073
+ }
1042
1074
 
1043
1075
  /** @public */
1044
1076
  export declare interface LiveEventMessage {
@@ -339,6 +339,8 @@ export declare interface ClientConfig {
339
339
  /** @defaultValue true */
340
340
  useCdn?: boolean
341
341
  token?: string
342
+ /** @internal */
343
+ '~experimental_resource'?: ClientConfigResource
342
344
  /**
343
345
  * What perspective to use for the client. See {@link https://www.sanity.io/docs/perspectives|perspective documentation}
344
346
  * @remarks
@@ -399,6 +401,24 @@ export declare interface ClientConfig {
399
401
  stega?: StegaConfig | boolean
400
402
  }
401
403
 
404
+ declare type ClientConfigResource =
405
+ | {
406
+ type: 'canvas'
407
+ id: string
408
+ }
409
+ | {
410
+ type: 'media-library'
411
+ id: string
412
+ }
413
+ | {
414
+ type: 'dataset'
415
+ id: string
416
+ }
417
+ | {
418
+ type: 'dashboard'
419
+ id: string
420
+ }
421
+
402
422
  /** @public */
403
423
  export declare class ClientError extends Error {
404
424
  response: ErrorProps['response']
@@ -1039,6 +1059,18 @@ export declare type LiveEvent =
1039
1059
  | LiveEventReconnect
1040
1060
  | LiveEventMessage
1041
1061
  | LiveEventWelcome
1062
+ | LiveEventGoAway
1063
+
1064
+ /**
1065
+ * The `id` field is the position at which the connection was rejected or closed.
1066
+ * The `reason` field will specify why the connection rejected/closed.
1067
+ * @public
1068
+ */
1069
+ export declare interface LiveEventGoAway {
1070
+ type: 'goaway'
1071
+ id: string
1072
+ reason: string
1073
+ }
1042
1074
 
1043
1075
  /** @public */
1044
1076
  export declare interface LiveEventMessage {
@@ -107,8 +107,8 @@ function defineHttpRequest(envMiddleware2) {
107
107
  }
108
108
  function shouldRetry(err, attempt, options) {
109
109
  if (options.maxRetries === 0) return !1;
110
- 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);
111
- return (isSafe || isQuery) && isRetriableResponse ? !0 : retry.shouldRetry(err, attempt, options);
110
+ 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);
111
+ return (isSafe || isQuery2) && isRetriableResponse ? !0 : retry.shouldRetry(err, attempt, options);
112
112
  }
113
113
  const BASE_URL = "https://www.sanity.io/help/";
114
114
  function generateHelpUrl(slug) {
@@ -155,6 +155,26 @@ const VALID_ASSET_TYPES = ["image", "file"], VALID_INSERT_LOCATIONS = ["before",
155
155
  "Tag can only contain alphanumeric characters, underscores, dashes and dots, and be between one and 75 characters long."
156
156
  );
157
157
  return tag;
158
+ }, resourceConfig = (config) => {
159
+ if (!config["~experimental_resource"])
160
+ throw new Error("`resource` must be provided to perform resource queries");
161
+ const { type, id } = config["~experimental_resource"];
162
+ switch (type) {
163
+ case "dataset": {
164
+ if (id.split(".").length !== 2)
165
+ throw new Error('Dataset resource ID must be in the format "project.dataset"');
166
+ return;
167
+ }
168
+ case "dashboard":
169
+ case "media-library":
170
+ case "canvas":
171
+ return;
172
+ default:
173
+ throw new Error(`Unsupported resource type: ${type.toString()}`);
174
+ }
175
+ }, resourceGuard = (service, config) => {
176
+ if (config["~experimental_resource"])
177
+ throw new Error(`\`${service}\` does not support resource-based operations`);
158
178
  };
159
179
  function once(fn) {
160
180
  let didCall = !1, returnValue;
@@ -220,14 +240,14 @@ const initConfig = (config, prevConfig) => {
220
240
  const newConfig = {
221
241
  ...defaultConfig,
222
242
  ...specifiedConfig
223
- }, projectBased = newConfig.useProjectHostname;
243
+ }, projectBased = newConfig.useProjectHostname && !newConfig["~experimental_resource"];
224
244
  if (typeof Promise > "u") {
225
245
  const helpUrl = generateHelpUrl("js-client-promise-polyfill");
226
246
  throw new Error(`No native Promise-implementation found, polyfill needed - see ${helpUrl}`);
227
247
  }
228
248
  if (projectBased && !newConfig.projectId)
229
249
  throw new Error("Configuration must contain `projectId`");
230
- if (typeof newConfig.perspective < "u" && validateApiPerspective(newConfig.perspective), "encodeSourceMap" in newConfig)
250
+ if (newConfig["~experimental_resource"] && resourceConfig(newConfig), typeof newConfig.perspective < "u" && validateApiPerspective(newConfig.perspective), "encodeSourceMap" in newConfig)
231
251
  throw new Error(
232
252
  "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'?"
233
253
  );
@@ -246,7 +266,7 @@ const initConfig = (config, prevConfig) => {
246
266
  const isBrowser = typeof window < "u" && window.location && window.location.hostname, isLocalhost = isBrowser && isLocal(window.location.hostname), hasToken = !!newConfig.token;
247
267
  newConfig.withCredentials && hasToken && (printCredentialedTokenWarning(), newConfig.withCredentials = !1), isBrowser && isLocalhost && hasToken && newConfig.ignoreBrowserTokenWarning !== !0 ? 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 === !0 && newConfig.withCredentials && printCdnAndWithCredentialsWarning(), newConfig.useCdn = newConfig.useCdn !== !1 && !newConfig.withCredentials, validateApiVersion(newConfig.apiVersion);
248
268
  const hostParts = newConfig.apiHost.split("://", 2), protocol = hostParts[0], host = hostParts[1], cdnHost = newConfig.isDefaultApi ? defaultCdnHost : host;
249
- 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;
269
+ 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;
250
270
  };
251
271
  class ConnectionFailedError extends Error {
252
272
  name = "ConnectionFailedError";
@@ -814,7 +834,7 @@ function _action(client, httpRequest, actions, options) {
814
834
  );
815
835
  }
816
836
  function _dataRequest(client, httpRequest, endpoint, body, options = {}) {
817
- 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 = {
837
+ 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 = {
818
838
  method: useGet ? "GET" : "POST",
819
839
  uri,
820
840
  json: !0,
@@ -829,7 +849,7 @@ function _dataRequest(client, httpRequest, endpoint, body, options = {}) {
829
849
  resultSourceMap: options.resultSourceMap,
830
850
  lastLiveEventId: Array.isArray(lastLiveEventId) ? lastLiveEventId[0] : lastLiveEventId,
831
851
  cacheMode,
832
- canUseCdn: isQuery,
852
+ canUseCdn: isQuery2,
833
853
  signal: options.signal,
834
854
  fetch: options.fetch,
835
855
  useAbortSignal: options.useAbortSignal,
@@ -857,11 +877,12 @@ function _create(client, httpRequest, doc, op, options = {}) {
857
877
  const mutation = { [op]: doc }, opts = Object.assign({ returnFirst: !0, returnDocuments: !0 }, options);
858
878
  return _dataRequest(client, httpRequest, "mutate", { mutations: [mutation] }, opts);
859
879
  }
880
+ const hasDataConfig = (client) => client.config().dataset !== void 0 && client.config().projectId !== void 0 || client.config()["~experimental_resource"] !== void 0, isQuery = (client, uri) => hasDataConfig(client) && uri.startsWith(_getDataUrl(client, "query")), isMutate = (client, uri) => hasDataConfig(client) && uri.startsWith(_getDataUrl(client, "mutate")), isDoc = (client, uri) => hasDataConfig(client) && uri.startsWith(_getDataUrl(client, "doc", "")), isListener = (client, uri) => hasDataConfig(client) && uri.startsWith(_getDataUrl(client, "listen")), isHistory = (client, uri) => hasDataConfig(client) && uri.startsWith(_getDataUrl(client, "history", "")), isData = (client, uri) => uri.startsWith("/data/") || isQuery(client, uri) || isMutate(client, uri) || isDoc(client, uri) || isListener(client, uri) || isHistory(client, uri);
860
881
  function _requestObservable(client, httpRequest, options) {
861
- 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;
882
+ const uri = options.url || options.uri, config = client.config(), canUseCdn = typeof options.canUseCdn > "u" ? ["GET", "HEAD"].indexOf(options.method || "GET") >= 0 && isData(client, uri) : options.canUseCdn;
862
883
  let useCdn = (options.useCdn ?? config.useCdn) && canUseCdn;
863
884
  const tag = options.tag && config.requestTagPrefix ? [config.requestTagPrefix, options.tag].join(".") : options.tag || config.requestTagPrefix;
864
- 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) {
885
+ if (tag && options.tag !== null && (options.query = { tag: requestTag(tag), ...options.query }), ["GET", "HEAD", "POST"].indexOf(options.method || "GET") >= 0 && isQuery(client, uri)) {
865
886
  const resultSourceMap = options.resultSourceMap ?? config.resultSourceMap;
866
887
  resultSourceMap !== void 0 && resultSourceMap !== !1 && (options.query = { resultSourceMap, ...options.query });
867
888
  const perspectiveOption = options.perspective || config.perspective;
@@ -888,8 +909,14 @@ function _request(client, httpRequest, options) {
888
909
  );
889
910
  }
890
911
  function _getDataUrl(client, operation, path) {
891
- const config = client.config(), catalog = hasDataset(config), baseUri = `/${operation}/${catalog}`;
892
- return `/data${path ? `${baseUri}/${path}` : baseUri}`.replace(/\/($|\?)/, "$1");
912
+ const config = client.config();
913
+ if (config["~experimental_resource"]) {
914
+ resourceConfig(config);
915
+ const resourceBase = resourceDataBase(config), uri2 = path !== void 0 ? `${operation}/${path}` : operation;
916
+ return `${resourceBase}/${uri2}`.replace(/\/($|\?)/, "$1");
917
+ }
918
+ const catalog = hasDataset(config), baseUri = `/${operation}/${catalog}`;
919
+ return `/data${path !== void 0 ? `${baseUri}/${path}` : baseUri}`.replace(/\/($|\?)/, "$1");
893
920
  }
894
921
  function _getUrl(client, uri, canUseCdn = !1) {
895
922
  const { url, cdnUrl } = client.config();
@@ -915,6 +942,27 @@ function _createAbortError(signal) {
915
942
  const error = new Error(signal?.reason ?? "The operation was aborted.");
916
943
  return error.name = "AbortError", error;
917
944
  }
945
+ const resourceDataBase = (config) => {
946
+ if (!config["~experimental_resource"])
947
+ throw new Error("`resource` must be provided to perform resource queries");
948
+ const { type, id } = config["~experimental_resource"];
949
+ switch (type) {
950
+ case "dataset": {
951
+ const segments = id.split(".");
952
+ if (segments.length !== 2)
953
+ throw new Error('Dataset ID must be in the format "project.dataset"');
954
+ return `/projects/${segments[0]}/datasets/${segments[1]}`;
955
+ }
956
+ case "canvas":
957
+ return `/canvases/${id}`;
958
+ case "media-library":
959
+ return `/media-libraries/${id}`;
960
+ case "dashboard":
961
+ return `/dashboards/${id}`;
962
+ default:
963
+ throw new Error(`Unsupported resource type: ${type.toString()}`);
964
+ }
965
+ };
918
966
  class ObservableAssetsClient {
919
967
  #client;
920
968
  #httpRequest;
@@ -947,7 +995,7 @@ function _upload(client, httpRequest, assetType, body, opts = {}) {
947
995
  validateAssetType(assetType);
948
996
  let meta = opts.extract || void 0;
949
997
  meta && !meta.length && (meta = ["none"]);
950
- const dataset2 = hasDataset(client.config()), assetEndpoint = assetType === "image" ? "images" : "files", options = optionsFromFile(opts, body), { tag, label, title, description, creditLine, filename, source } = options, query = {
998
+ const config = client.config(), options = optionsFromFile(opts, body), { tag, label, title, description, creditLine, filename, source } = options, query = {
951
999
  label,
952
1000
  title,
953
1001
  description,
@@ -959,12 +1007,34 @@ function _upload(client, httpRequest, assetType, body, opts = {}) {
959
1007
  tag,
960
1008
  method: "POST",
961
1009
  timeout: options.timeout || 0,
962
- uri: `/assets/${assetEndpoint}/${dataset2}`,
1010
+ uri: buildAssetUploadUrl(config, assetType),
963
1011
  headers: options.contentType ? { "Content-Type": options.contentType } : {},
964
1012
  query,
965
1013
  body
966
1014
  });
967
1015
  }
1016
+ function buildAssetUploadUrl(config, assetType) {
1017
+ const assetTypeEndpoint = assetType === "image" ? "images" : "files";
1018
+ if (config["~experimental_resource"]) {
1019
+ const { type, id } = config["~experimental_resource"];
1020
+ switch (type) {
1021
+ case "dataset":
1022
+ throw new Error(
1023
+ "Assets are not supported for dataset resources, yet. Configure the client with `{projectId: <projectId>, dataset: <datasetId>}` instead."
1024
+ );
1025
+ case "canvas":
1026
+ return `/canvases/${id}/assets/${assetTypeEndpoint}`;
1027
+ case "media-library":
1028
+ return `/media-libraries/${id}/upload`;
1029
+ case "dashboard":
1030
+ return `/dashboards/${id}/assets/${assetTypeEndpoint}`;
1031
+ default:
1032
+ throw new Error(`Unsupported resource type: ${type.toString()}`);
1033
+ }
1034
+ }
1035
+ const dataset2 = hasDataset(config);
1036
+ return `assets/${assetTypeEndpoint}/${dataset2}`;
1037
+ }
968
1038
  function optionsFromFile(opts, file) {
969
1039
  return typeof File > "u" || !(file instanceof File) ? opts : Object.assign(
970
1040
  {
@@ -1056,6 +1126,7 @@ class LiveClient {
1056
1126
  includeDrafts = !1,
1057
1127
  tag: _tag
1058
1128
  } = {}) {
1129
+ resourceGuard("live", this.#client.config());
1059
1130
  const {
1060
1131
  projectId: projectId2,
1061
1132
  apiVersion: _apiVersion,
@@ -1087,7 +1158,8 @@ class LiveClient {
1087
1158
  "message",
1088
1159
  "restart",
1089
1160
  "welcome",
1090
- "reconnect"
1161
+ "reconnect",
1162
+ "goaway"
1091
1163
  ]).pipe(
1092
1164
  reconnectOnConnectionFailure(),
1093
1165
  map((event) => {
@@ -1185,7 +1257,7 @@ class DatasetsClient {
1185
1257
  * @param options - Options for the dataset
1186
1258
  */
1187
1259
  create(name, options) {
1188
- return lastValueFrom(
1260
+ return resourceGuard("dataset", this.#client.config()), lastValueFrom(
1189
1261
  _modify(this.#client, this.#httpRequest, "PUT", name, options)
1190
1262
  );
1191
1263
  }
@@ -1196,7 +1268,7 @@ class DatasetsClient {
1196
1268
  * @param options - New options for the dataset
1197
1269
  */
1198
1270
  edit(name, options) {
1199
- return lastValueFrom(
1271
+ return resourceGuard("dataset", this.#client.config()), lastValueFrom(
1200
1272
  _modify(this.#client, this.#httpRequest, "PATCH", name, options)
1201
1273
  );
1202
1274
  }
@@ -1206,19 +1278,19 @@ class DatasetsClient {
1206
1278
  * @param name - Name of the dataset to delete
1207
1279
  */
1208
1280
  delete(name) {
1209
- return lastValueFrom(_modify(this.#client, this.#httpRequest, "DELETE", name));
1281
+ return resourceGuard("dataset", this.#client.config()), lastValueFrom(_modify(this.#client, this.#httpRequest, "DELETE", name));
1210
1282
  }
1211
1283
  /**
1212
1284
  * Fetch a list of datasets for the configured project
1213
1285
  */
1214
1286
  list() {
1215
- return lastValueFrom(
1287
+ return resourceGuard("dataset", this.#client.config()), lastValueFrom(
1216
1288
  _request(this.#client, this.#httpRequest, { uri: "/datasets", tag: null })
1217
1289
  );
1218
1290
  }
1219
1291
  }
1220
1292
  function _modify(client, httpRequest, method, name, options) {
1221
- return dataset(name), _request(client, httpRequest, {
1293
+ return resourceGuard("dataset", client.config()), dataset(name), _request(client, httpRequest, {
1222
1294
  method,
1223
1295
  uri: `/datasets/${name}`,
1224
1296
  body: options,
@@ -1232,6 +1304,7 @@ class ObservableProjectsClient {
1232
1304
  this.#client = client, this.#httpRequest = httpRequest;
1233
1305
  }
1234
1306
  list(options) {
1307
+ resourceGuard("projects", this.#client.config());
1235
1308
  const uri = options?.includeMembers === !1 ? "/projects?includeMembers=false" : "/projects";
1236
1309
  return _request(this.#client, this.#httpRequest, { uri });
1237
1310
  }
@@ -1241,7 +1314,7 @@ class ObservableProjectsClient {
1241
1314
  * @param projectId - ID of the project to fetch
1242
1315
  */
1243
1316
  getById(projectId2) {
1244
- return _request(this.#client, this.#httpRequest, { uri: `/projects/${projectId2}` });
1317
+ return resourceGuard("projects", this.#client.config()), _request(this.#client, this.#httpRequest, { uri: `/projects/${projectId2}` });
1245
1318
  }
1246
1319
  }
1247
1320
  class ProjectsClient {
@@ -1251,6 +1324,7 @@ class ProjectsClient {
1251
1324
  this.#client = client, this.#httpRequest = httpRequest;
1252
1325
  }
1253
1326
  list(options) {
1327
+ resourceGuard("projects", this.#client.config());
1254
1328
  const uri = options?.includeMembers === !1 ? "/projects?includeMembers=false" : "/projects";
1255
1329
  return lastValueFrom(_request(this.#client, this.#httpRequest, { uri }));
1256
1330
  }
@@ -1260,7 +1334,7 @@ class ProjectsClient {
1260
1334
  * @param projectId - ID of the project to fetch
1261
1335
  */
1262
1336
  getById(projectId2) {
1263
- return lastValueFrom(
1337
+ return resourceGuard("projects", this.#client.config()), lastValueFrom(
1264
1338
  _request(this.#client, this.#httpRequest, { uri: `/projects/${projectId2}` })
1265
1339
  );
1266
1340
  }