@sanity/client 6.24.0 → 6.25.0-alpha.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.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { getIt } from "get-it";
2
2
  import { adapter, environment } from "get-it";
3
3
  import { retry, jsonRequest, jsonResponse, progress, observable, debug, headers, agent } from "get-it/middleware";
4
- import { Observable, from, lastValueFrom } from "rxjs";
4
+ import { Observable, defer, isObservable, of, mergeMap, from, lastValueFrom, shareReplay, catchError, concat, timer, throwError, EMPTY } from "rxjs";
5
5
  import { stegaClean } from "./_chunks-es/stegaClean.js";
6
6
  import { combineLatestWith, map, filter } from "rxjs/operators";
7
7
  class ClientError extends Error {
@@ -191,7 +191,7 @@ function validateApiVersion(apiVersion) {
191
191
  if (!(/^\d{4}-\d{2}-\d{2}$/.test(apiVersion) && apiDate instanceof Date && apiDate.getTime() > 0))
192
192
  throw new Error("Invalid API version string, expected `1` or date in format `YYYY-MM-DD`");
193
193
  }
194
- const validateApiPerspective = function(perspective) {
194
+ function validateApiPerspective(perspective) {
195
195
  if (Array.isArray(perspective)) {
196
196
  for (const perspectiveValue of perspective)
197
197
  if (perspectiveValue !== "published" && perspectiveValue !== "drafts" && !(typeof perspectiveValue == "string" && perspectiveValue.startsWith("r") && perspectiveValue !== "raw"))
@@ -211,7 +211,8 @@ const validateApiPerspective = function(perspective) {
211
211
  "Invalid API perspective string, expected `published`, `previewDrafts` or `raw`"
212
212
  );
213
213
  }
214
- }, initConfig = (config, prevConfig) => {
214
+ }
215
+ const initConfig = (config, prevConfig) => {
215
216
  const specifiedConfig = {
216
217
  ...prevConfig,
217
218
  ...config,
@@ -252,6 +253,112 @@ const validateApiPerspective = function(perspective) {
252
253
  const hostParts = newConfig.apiHost.split("://", 2), protocol = hostParts[0], host = hostParts[1], cdnHost = newConfig.isDefaultApi ? defaultCdnHost : host;
253
254
  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;
254
255
  };
256
+ class ConnectionFailedError extends Error {
257
+ name = "ConnectionFailedError";
258
+ }
259
+ class DisconnectError extends Error {
260
+ name = "DisconnectError";
261
+ reason;
262
+ constructor(message, reason, options = {}) {
263
+ super(message, options), this.reason = reason;
264
+ }
265
+ }
266
+ class ChannelError extends Error {
267
+ name = "ChannelError";
268
+ data;
269
+ constructor(message, data) {
270
+ super(message), this.data = data;
271
+ }
272
+ }
273
+ class MessageError extends Error {
274
+ name = "MessageError";
275
+ data;
276
+ constructor(message, data, options = {}) {
277
+ super(message, options), this.data = data;
278
+ }
279
+ }
280
+ class MessageParseError extends Error {
281
+ name = "MessageParseError";
282
+ }
283
+ const REQUIRED_EVENTS = ["channelError", "disconnect"];
284
+ function connectEventSource(initEventSource, events) {
285
+ return defer(() => {
286
+ const es = initEventSource();
287
+ return isObservable(es) ? es : of(es);
288
+ }).pipe(mergeMap((es) => connectWithESInstance(es, events)));
289
+ }
290
+ function connectWithESInstance(es, events) {
291
+ return new Observable((observer) => {
292
+ const emitOpen = events.includes("open"), emitReconnect = events.includes("reconnect");
293
+ function onError(evt) {
294
+ if ("data" in evt) {
295
+ const [parseError, event] = parseEvent(evt);
296
+ observer.error(
297
+ parseError ? new MessageParseError("Unable to parse EventSource error message", { cause: event }) : new MessageError((event?.data).message, event)
298
+ );
299
+ return;
300
+ }
301
+ es.readyState === es.CLOSED ? observer.error(new ConnectionFailedError("EventSource connection failed")) : emitReconnect && observer.next({ type: "reconnect" });
302
+ }
303
+ function onOpen() {
304
+ observer.next({ type: "open" });
305
+ }
306
+ function onMessage(message) {
307
+ const [parseError, event] = parseEvent(message);
308
+ if (parseError) {
309
+ observer.error(
310
+ new MessageParseError("Unable to parse EventSource message", { cause: parseError })
311
+ );
312
+ return;
313
+ }
314
+ if (message.type === "channelError") {
315
+ observer.error(new ChannelError(extractErrorMessage(event?.data), event.data));
316
+ return;
317
+ }
318
+ if (message.type === "disconnect") {
319
+ observer.error(
320
+ new DisconnectError(
321
+ `Server disconnected client: ${event.data?.reason || "unknown error"}`
322
+ )
323
+ );
324
+ return;
325
+ }
326
+ observer.next({
327
+ type: message.type,
328
+ id: message.lastEventId,
329
+ ...event.data ? { data: event.data } : {}
330
+ });
331
+ }
332
+ es.addEventListener("error", onError), emitOpen && es.addEventListener("open", onOpen);
333
+ const cleanedEvents = [.../* @__PURE__ */ new Set([...REQUIRED_EVENTS, ...events])].filter((type) => type !== "error" && type !== "open" && type !== "reconnect");
334
+ return cleanedEvents.forEach((type) => es.addEventListener(type, onMessage)), () => {
335
+ es.removeEventListener("error", onError), emitOpen && es.removeEventListener("open", onOpen), cleanedEvents.forEach((type) => es.removeEventListener(type, onMessage)), es.close();
336
+ };
337
+ });
338
+ }
339
+ function parseEvent(message) {
340
+ try {
341
+ const data = typeof message.data == "string" && JSON.parse(message.data);
342
+ return [
343
+ null,
344
+ {
345
+ type: message.type,
346
+ id: message.lastEventId,
347
+ ...isEmptyObject(data) ? {} : { data }
348
+ }
349
+ ];
350
+ } catch (err) {
351
+ return [err, null];
352
+ }
353
+ }
354
+ function extractErrorMessage(err) {
355
+ return err.error ? err.error.description ? err.error.description : typeof err.error == "string" ? err.error : JSON.stringify(err.error, null, 2) : err.message || "Unknown listener error";
356
+ }
357
+ function isEmptyObject(data) {
358
+ for (const _ in data)
359
+ return !1;
360
+ return !0;
361
+ }
255
362
  function getSelection(sel) {
256
363
  if (typeof sel == "string")
257
364
  return { id: sel };
@@ -868,7 +975,18 @@ function optionsFromFile(opts, file) {
868
975
  );
869
976
  }
870
977
  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), {});
871
- const pick = (obj, props) => props.reduce((selection, prop) => (typeof obj[prop] > "u" || (selection[prop] = obj[prop]), selection), {}), MAX_URL_LENGTH = 14800, possibleOptions = [
978
+ const pick = (obj, props) => props.reduce((selection, prop) => (typeof obj[prop] > "u" || (selection[prop] = obj[prop]), selection), {}), eventSourcePolyfill = defer(() => import("@sanity/eventsource")).pipe(
979
+ map(({ default: EventSource2 }) => EventSource2),
980
+ shareReplay(1)
981
+ );
982
+ function reconnectOnConnectionFailure() {
983
+ return function(source) {
984
+ return source.pipe(
985
+ catchError((err, caught) => err instanceof ConnectionFailedError ? concat(of({ type: "reconnect" }), timer(1e3).pipe(mergeMap(() => caught))) : throwError(() => err))
986
+ );
987
+ };
988
+ }
989
+ const MAX_URL_LENGTH = 14800, possibleOptions = [
872
990
  "includePreviousRevision",
873
991
  "includeResult",
874
992
  "includeMutations",
@@ -881,68 +999,23 @@ const pick = (obj, props) => props.reduce((selection, prop) => (typeof obj[prop]
881
999
  function _listen(query, params, opts = {}) {
882
1000
  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)}`;
883
1001
  if (uri.length > MAX_URL_LENGTH)
884
- return new Observable((observer) => observer.error(new Error("Query too large for listener")));
885
- const listenFor = options.events ? options.events : ["mutation"], shouldEmitReconnect = listenFor.indexOf("reconnect") !== -1, esOptions = {};
1002
+ return throwError(() => new Error("Query too large for listener"));
1003
+ const listenFor = options.events ? options.events : ["mutation"], esOptions = {};
886
1004
  return (token || withCredentials) && (esOptions.withCredentials = !0), token && (esOptions.headers = {
887
1005
  Authorization: `Bearer ${token}`
888
- }), new Observable((observer) => {
889
- let es, reconnectTimer, stopped = !1, unsubscribed = !1;
890
- open();
891
- function onError() {
892
- stopped || (emitReconnect(), !stopped && es.readyState === es.CLOSED && (unsubscribe(), clearTimeout(reconnectTimer), reconnectTimer = setTimeout(open, 100)));
893
- }
894
- function onChannelError(err) {
895
- observer.error(cooerceError(err));
896
- }
897
- function onMessage(evt) {
898
- const event = parseEvent$1(evt);
899
- return event instanceof Error ? observer.error(event) : observer.next(event);
900
- }
901
- function onDisconnect() {
902
- stopped = !0, unsubscribe(), observer.complete();
903
- }
904
- function unsubscribe() {
905
- es && (es.removeEventListener("error", onError), es.removeEventListener("channelError", onChannelError), es.removeEventListener("disconnect", onDisconnect), listenFor.forEach((type) => es.removeEventListener(type, onMessage)), es.close());
906
- }
907
- function emitReconnect() {
908
- shouldEmitReconnect && observer.next({ type: "reconnect" });
909
- }
910
- async function getEventSource() {
911
- const { default: EventSource2 } = await import("@sanity/eventsource");
912
- if (unsubscribed)
913
- return;
914
- const evs = new EventSource2(uri, esOptions);
915
- return evs.addEventListener("error", onError), evs.addEventListener("channelError", onChannelError), evs.addEventListener("disconnect", onDisconnect), listenFor.forEach((type) => evs.addEventListener(type, onMessage)), evs;
916
- }
917
- function open() {
918
- getEventSource().then((eventSource) => {
919
- eventSource && (es = eventSource, unsubscribed && unsubscribe());
920
- }).catch((reason) => {
921
- observer.error(reason), stop();
922
- });
923
- }
924
- function stop() {
925
- stopped = !0, unsubscribe(), unsubscribed = !0;
926
- }
927
- return stop;
928
- });
929
- }
930
- function parseEvent$1(event) {
931
- try {
932
- const data = event.data && JSON.parse(event.data) || {};
933
- return Object.assign({ type: event.type }, data);
934
- } catch (err) {
935
- return err;
936
- }
937
- }
938
- function cooerceError(err) {
939
- if (err instanceof Error)
940
- return err;
941
- const evt = parseEvent$1(err);
942
- return evt instanceof Error ? evt : new Error(extractErrorMessage(evt));
943
- }
944
- function extractErrorMessage(err) {
945
- return err.error ? err.error.description ? err.error.description : typeof err.error == "string" ? err.error : JSON.stringify(err.error, null, 2) : err.message || "Unknown listener error";
1006
+ }), connectEventSource(() => (
1007
+ // use polyfill if there is no global EventSource or if we need to set headers
1008
+ (typeof EventSource > "u" || esOptions.headers ? eventSourcePolyfill : of(EventSource)).pipe(map((EventSource2) => new EventSource2(uri, esOptions)))
1009
+ ), listenFor).pipe(
1010
+ reconnectOnConnectionFailure(),
1011
+ filter((event) => listenFor.includes(event.type)),
1012
+ map(
1013
+ (event) => ({
1014
+ type: event.type,
1015
+ ..."data" in event ? event.data : {}
1016
+ })
1017
+ )
1018
+ );
946
1019
  }
947
1020
  const requiredApiVersion = "2021-03-26";
948
1021
  class LiveClient {
@@ -978,75 +1051,53 @@ class LiveClient {
978
1051
  );
979
1052
  const path = _getDataUrl(this.#client, "live/events"), url = new URL(this.#client.getUrl(path, !1)), tag = _tag && requestTagPrefix ? [requestTagPrefix, _tag].join(".") : _tag;
980
1053
  tag && url.searchParams.set("tag", tag), includeDrafts && url.searchParams.set("includeDrafts", "true");
981
- const listenFor = ["restart", "message", "welcome", "reconnect"], esOptions = {};
982
- return includeDrafts && token && (esOptions.headers = {
1054
+ const esOptions = {};
1055
+ includeDrafts && token && (esOptions.headers = {
983
1056
  Authorization: `Bearer ${token}`
984
- }), includeDrafts && withCredentials && (esOptions.withCredentials = !0), new Observable((observer) => {
985
- let es, reconnectTimer, stopped = !1, unsubscribed = !1;
986
- open();
987
- function onError(evt) {
988
- if (!stopped) {
989
- if ("data" in evt) {
990
- const event = parseEvent(evt);
991
- observer.error(new Error(event.message, { cause: event }));
992
- }
993
- es.readyState === es.CLOSED && (unsubscribe(), clearTimeout(reconnectTimer), reconnectTimer = setTimeout(open, 100));
994
- }
995
- }
996
- function onMessage(evt) {
997
- const event = parseEvent(evt);
998
- return event instanceof Error ? observer.error(event) : observer.next(event);
999
- }
1000
- function unsubscribe() {
1001
- if (es) {
1002
- es.removeEventListener("error", onError);
1003
- for (const type of listenFor)
1004
- es.removeEventListener(type, onMessage);
1005
- es.close();
1006
- }
1007
- }
1008
- async function getEventSource() {
1009
- const EventSourceImplementation = typeof EventSource > "u" || esOptions.headers || esOptions.withCredentials ? (await import("@sanity/eventsource")).default : EventSource;
1010
- if (unsubscribed)
1011
- return;
1012
- try {
1013
- if (await fetch(url, {
1014
- method: "OPTIONS",
1015
- mode: "cors",
1016
- credentials: esOptions.withCredentials ? "include" : "omit",
1017
- headers: esOptions.headers
1018
- }), unsubscribed)
1019
- return;
1020
- } catch {
1021
- throw new CorsOriginError({ projectId: projectId2 });
1057
+ }), includeDrafts && withCredentials && (esOptions.withCredentials = !0);
1058
+ const events = connectEventSource(() => (
1059
+ // use polyfill if there is no global EventSource or if we need to set headers
1060
+ (typeof EventSource > "u" || esOptions.headers ? eventSourcePolyfill : of(EventSource)).pipe(map((EventSource2) => new EventSource2(url.href, esOptions)))
1061
+ ), [
1062
+ "message",
1063
+ "restart",
1064
+ "welcome",
1065
+ "reconnect"
1066
+ ]).pipe(
1067
+ reconnectOnConnectionFailure(),
1068
+ map((event) => {
1069
+ if (event.type === "message") {
1070
+ const { data, ...rest } = event;
1071
+ return { ...rest, tags: data.tags };
1022
1072
  }
1023
- const evs = new EventSourceImplementation(url.toString(), esOptions);
1024
- evs.addEventListener("error", onError);
1025
- for (const type of listenFor)
1026
- evs.addEventListener(type, onMessage);
1027
- return evs;
1028
- }
1029
- function open() {
1030
- getEventSource().then((eventSource) => {
1031
- eventSource && (es = eventSource, unsubscribed && unsubscribe());
1032
- }).catch((reason) => {
1033
- observer.error(reason), stop();
1034
- });
1035
- }
1036
- function stop() {
1037
- stopped = !0, unsubscribe(), unsubscribed = !0;
1038
- }
1039
- return stop;
1040
- });
1073
+ return event;
1074
+ })
1075
+ ), checkCors = fetchObservable(url, {
1076
+ method: "OPTIONS",
1077
+ mode: "cors",
1078
+ credentials: esOptions.withCredentials ? "include" : "omit",
1079
+ headers: esOptions.headers
1080
+ }).pipe(
1081
+ mergeMap(() => EMPTY),
1082
+ catchError(() => {
1083
+ throw new CorsOriginError({ projectId: projectId2 });
1084
+ })
1085
+ );
1086
+ return concat(checkCors, events);
1041
1087
  }
1042
1088
  }
1043
- function parseEvent(event) {
1044
- try {
1045
- const data = event.data && JSON.parse(event.data) || {};
1046
- return { type: event.type, id: event.lastEventId, ...data };
1047
- } catch (err) {
1048
- return err;
1049
- }
1089
+ function fetchObservable(url, init) {
1090
+ return new Observable((observer) => {
1091
+ const controller = new AbortController(), signal = controller.signal;
1092
+ return fetch(url, { ...init, signal: controller.signal }).then(
1093
+ (response) => {
1094
+ observer.next(response), observer.complete();
1095
+ },
1096
+ (err) => {
1097
+ signal.aborted || observer.error(err);
1098
+ }
1099
+ ), () => controller.abort();
1100
+ });
1050
1101
  }
1051
1102
  class ObservableDatasetsClient {
1052
1103
  #client;
@@ -1564,7 +1615,7 @@ function defineDeprecatedCreateClient(createClient2) {
1564
1615
  return printNoDefaultExport(), createClient2(config);
1565
1616
  };
1566
1617
  }
1567
- var name = "@sanity/client", version = "6.24.0";
1618
+ var name = "@sanity/client", version = "6.25.0-alpha.0";
1568
1619
  const middleware = [
1569
1620
  debug({ verbose: !0, namespace: "sanity:client" }),
1570
1621
  headers({ "User-Agent": `${name} ${version}` }),
@@ -1585,8 +1636,13 @@ const middleware = [
1585
1636
  export {
1586
1637
  BasePatch,
1587
1638
  BaseTransaction,
1639
+ ChannelError,
1588
1640
  ClientError,
1641
+ ConnectionFailedError,
1589
1642
  CorsOriginError,
1643
+ DisconnectError,
1644
+ MessageError,
1645
+ MessageParseError,
1590
1646
  ObservablePatch,
1591
1647
  ObservableSanityClient,
1592
1648
  ObservableTransaction,
@@ -1594,6 +1650,7 @@ export {
1594
1650
  SanityClient,
1595
1651
  ServerError,
1596
1652
  Transaction,
1653
+ connectEventSource,
1597
1654
  createClient,
1598
1655
  deprecatedCreateClient as default,
1599
1656
  requester,