@sanity/client 6.20.0 → 6.20.2-beta.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.
@@ -336,6 +336,13 @@ export declare interface ClientConfig {
336
336
  apiHost?: string
337
337
  apiVersion?: string
338
338
  proxy?: string
339
+ /**
340
+ * Spread the requests over a number of hostnames to work around HTTP/1.1 limitations.
341
+ * Only applicable in browsers, and for certain allowed projects.
342
+ *
343
+ * @alpha
344
+ */
345
+ useDomainSharding?: boolean
339
346
  /**
340
347
  * Optional request tag prefix for all request tags
341
348
  */
@@ -336,6 +336,13 @@ export declare interface ClientConfig {
336
336
  apiHost?: string
337
337
  apiVersion?: string
338
338
  proxy?: string
339
+ /**
340
+ * Spread the requests over a number of hostnames to work around HTTP/1.1 limitations.
341
+ * Only applicable in browsers, and for certain allowed projects.
342
+ *
343
+ * @alpha
344
+ */
345
+ useDomainSharding?: boolean
339
346
  /**
340
347
  * Optional request tag prefix for all request tags
341
348
  */
@@ -491,7 +491,10 @@ function once(fn) {
491
491
  const createWarningPrinter = (message) => (
492
492
  // eslint-disable-next-line no-console
493
493
  once((...args) => console.warn(message.join(" "), ...args))
494
- ), printCdnWarning = createWarningPrinter([
494
+ ), printCdnAndWithCredentialsWarning = createWarningPrinter([
495
+ "Because you set `withCredentials` to true, we will override your `useCdn`",
496
+ "setting to be false since (cookie-based) credentials are never set on the CDN"
497
+ ]), printCdnWarning = createWarningPrinter([
495
498
  "Since you haven't set a value for `useCdn`, we will deliver content using our",
496
499
  "global, edge-cached API-CDN. If you wish to have content delivered faster, set",
497
500
  "`useCdn: false` to use the Live API. Note: You may incur higher costs using the live API."
@@ -569,7 +572,7 @@ const validateApiPerspective = function(perspective) {
569
572
  `stega.studioUrl must be a string or a function, received ${newConfig.stega.studioUrl}`
570
573
  );
571
574
  const isBrowser = typeof window < "u" && window.location && window.location.hostname, isLocalhost = isBrowser && isLocal(window.location.hostname);
572
- isBrowser && isLocalhost && newConfig.token && 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 = newConfig.useCdn !== !1 && !newConfig.withCredentials, validateApiVersion(newConfig.apiVersion);
575
+ isBrowser && isLocalhost && newConfig.token && 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);
573
576
  const hostParts = newConfig.apiHost.split("://", 2), protocol = hostParts[0], host = hostParts[1], cdnHost = newConfig.isDefaultApi ? defaultCdnHost : host;
574
577
  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;
575
578
  }, projectHeader = "X-Sanity-Project-ID";
@@ -852,6 +855,55 @@ function optionsFromFile(opts, file) {
852
855
  opts
853
856
  );
854
857
  }
858
+ const UNSHARDED_URL_RE = /^https:\/\/([a-z0-9]+)\.api\.(sanity\..*)/, SHARDED_URL_RE = /^https:\/\/[a-z0-9]+\.api\.s(\d+)\.sanity\.(.*)/, domainSharder = getDomainSharder();
859
+ function getDomainSharder(initialBuckets) {
860
+ const buckets = new Array(10).fill(0, 0);
861
+ function incrementBucketForUrl(url) {
862
+ const shard = getShardFromUrl(url);
863
+ shard !== null && buckets[shard]++;
864
+ }
865
+ function decrementBucketForUrl(url) {
866
+ const shard = getShardFromUrl(url);
867
+ shard !== null && buckets[shard]--;
868
+ }
869
+ function getShardedUrl(url) {
870
+ const [isMatch, projectId2, rest] = url.match(UNSHARDED_URL_RE) || [];
871
+ if (!isMatch)
872
+ return url;
873
+ const bucket = buckets.reduce(
874
+ (smallest, count, index) => count < buckets[smallest] ? index : smallest,
875
+ 0
876
+ );
877
+ return `https://${projectId2}.api.s${bucket}.${rest}`;
878
+ }
879
+ function getShardFromUrl(url) {
880
+ const [isMatch, shard] = url.match(SHARDED_URL_RE) || [];
881
+ return isMatch ? parseInt(shard, 10) : null;
882
+ }
883
+ return {
884
+ middleware: {
885
+ processOptions: (options) => {
886
+ if (!useDomainSharding(options))
887
+ return options;
888
+ const url = getShardedUrl(options.url);
889
+ return options.url = url, options;
890
+ },
891
+ onRequest(req) {
892
+ return useDomainSharding(req.options) && incrementBucketForUrl(req.options.url), req;
893
+ },
894
+ onResponse(res, context) {
895
+ return useDomainSharding(context.options) && decrementBucketForUrl(context.options.url), res;
896
+ }
897
+ },
898
+ incrementBucketForUrl,
899
+ decrementBucketForUrl,
900
+ getShardedUrl,
901
+ getBuckets: () => buckets
902
+ };
903
+ }
904
+ function useDomainSharding(options) {
905
+ return "useDomainSharding" in options && options.useDomainSharding === !0;
906
+ }
855
907
  var defaults = (obj, defaults2) => Object.keys(defaults2).concat(Object.keys(obj)).reduce((target, prop) => (target[prop] = typeof obj[prop] > "u" ? defaults2[prop] : obj[prop], target), {});
856
908
  const pick = (obj, props) => props.reduce((selection, prop) => (typeof obj[prop] > "u" || (selection[prop] = obj[prop]), selection), {}), MAX_URL_LENGTH = 14800, possibleOptions = [
857
909
  "includePreviousRevision",
@@ -863,15 +915,16 @@ const pick = (obj, props) => props.reduce((selection, prop) => (typeof obj[prop]
863
915
  includeResult: !0
864
916
  };
865
917
  function _listen(query, params, opts = {}) {
866
- const { url, token, withCredentials, requestTagPrefix } = this.config(), tag = opts.tag && requestTagPrefix ? [requestTagPrefix, opts.tag].join(".") : opts.tag, options = { ...defaults(opts, defaultOptions), tag }, listenOpts = pick(options, possibleOptions), qs = encodeQueryString({ query, params, options: { tag, ...listenOpts } }), uri = `${url}${_getDataUrl(this, "listen", qs)}`;
867
- if (uri.length > MAX_URL_LENGTH)
918
+ const { url, token, withCredentials, requestTagPrefix } = this.config(), tag = opts.tag && requestTagPrefix ? [requestTagPrefix, opts.tag].join(".") : opts.tag, options = { ...defaults(opts, defaultOptions), tag }, listenOpts = pick(options, possibleOptions), qs = encodeQueryString({ query, params, options: { tag, ...listenOpts } });
919
+ let uri = `${url}${_getDataUrl(this, "listen", qs)}`;
920
+ if (this.config().useDomainSharding && (uri = domainSharder.getShardedUrl(uri)), uri.length > MAX_URL_LENGTH)
868
921
  return new Observable((observer) => observer.error(new Error("Query too large for listener")));
869
922
  const listenFor = options.events ? options.events : ["mutation"], shouldEmitReconnect = listenFor.indexOf("reconnect") !== -1, esOptions = {};
870
923
  return (token || withCredentials) && (esOptions.withCredentials = !0), token && (esOptions.headers = {
871
924
  Authorization: `Bearer ${token}`
872
925
  }), new Observable((observer) => {
873
926
  let es, reconnectTimer, stopped = !1, unsubscribed = !1;
874
- open();
927
+ domainSharder.incrementBucketForUrl(uri), open();
875
928
  function onError() {
876
929
  stopped || (emitReconnect(), !stopped && es.readyState === es.CLOSED && (unsubscribe(), clearTimeout(reconnectTimer), reconnectTimer = setTimeout(open, 100)));
877
930
  }
@@ -906,7 +959,7 @@ function _listen(query, params, opts = {}) {
906
959
  });
907
960
  }
908
961
  function stop() {
909
- stopped = !0, unsubscribe(), unsubscribed = !0;
962
+ stopped = !0, unsubscribe(), unsubscribed = !0, domainSharder.decrementBucketForUrl(uri);
910
963
  }
911
964
  return stop;
912
965
  });
@@ -1517,6 +1570,7 @@ function defineCreateClientExports(envMiddleware2, ClassConstructor) {
1517
1570
  maxRedirects: 0,
1518
1571
  maxRetries: config.maxRetries,
1519
1572
  retryDelay: config.retryDelay,
1573
+ useDomainSharding: config.useDomainSharding,
1520
1574
  ...options
1521
1575
  }),
1522
1576
  config
@@ -1527,7 +1581,7 @@ function defineDeprecatedCreateClient(createClient2) {
1527
1581
  return printNoDefaultExport(), createClient2(config);
1528
1582
  };
1529
1583
  }
1530
- var envMiddleware = [];
1584
+ var envMiddleware = [domainSharder.middleware];
1531
1585
  const exp = defineCreateClientExports(envMiddleware, SanityClient), requester = exp.requester, createClient = exp.createClient, deprecatedCreateClient = defineDeprecatedCreateClient(createClient);
1532
1586
  export {
1533
1587
  BasePatch,