@usethrottle/catalog 1.1.0 → 1.3.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +135 -24
  3. package/dist/admin.cjs +16 -16
  4. package/dist/admin.d.cts +6 -2
  5. package/dist/admin.d.ts +6 -2
  6. package/dist/admin.js +1 -1
  7. package/dist/{catalog-error-s1BINNkt.d.cts → catalog-error-BOaSKgY3.d.cts} +12 -2
  8. package/dist/{catalog-error-C_zLOtYA.d.ts → catalog-error-CSoPS0-d.d.ts} +12 -2
  9. package/dist/{chunk-4ORXUW2U.cjs → chunk-2YQNMBDI.cjs} +119 -33
  10. package/dist/{chunk-JK2UY4CD.js → chunk-3DLH56OX.js} +99 -13
  11. package/dist/chunk-CADIHOIR.js +496 -0
  12. package/dist/chunk-GU63WFXI.cjs +496 -0
  13. package/dist/{client-c2F-eCaL.d.ts → client-6j1Iz-iJ.d.cts} +2 -9
  14. package/dist/{client-BAoq6jmM.d.cts → client-B3mL2-xJ.d.ts} +2 -9
  15. package/dist/{contract-types-DhiThMEr.d.cts → contract-types-BRyonOYH.d.cts} +23 -21
  16. package/dist/{contract-types-DhiThMEr.d.ts → contract-types-BRyonOYH.d.ts} +23 -21
  17. package/dist/index.cjs +24 -2
  18. package/dist/index.d.cts +13 -7
  19. package/dist/index.d.ts +13 -7
  20. package/dist/index.js +25 -3
  21. package/dist/react-extras.cjs +42 -0
  22. package/dist/react-extras.d.cts +23 -0
  23. package/dist/react-extras.d.ts +23 -0
  24. package/dist/react-extras.js +42 -0
  25. package/dist/react.cjs +286 -491
  26. package/dist/react.d.cts +44 -38
  27. package/dist/react.d.ts +44 -38
  28. package/dist/react.js +289 -494
  29. package/dist/server.cjs +19 -14
  30. package/dist/server.d.cts +4 -3
  31. package/dist/server.d.ts +4 -3
  32. package/dist/server.js +7 -2
  33. package/dist/{types-DsjoG8ce.d.cts → types-BfveENg1.d.ts} +76 -3
  34. package/dist/{types-xQLID-4j.d.ts → types-D0PywKnN.d.cts} +76 -3
  35. package/dist/url.d.cts +2 -1
  36. package/dist/url.d.ts +2 -1
  37. package/dist/use-query-CR7r5tam.d.ts +40 -0
  38. package/dist/use-query-UWf6Pi5g.d.cts +40 -0
  39. package/dist/version-ev3p03v2.d.cts +8 -0
  40. package/dist/version-ev3p03v2.d.ts +8 -0
  41. package/package.json +13 -2
@@ -1,9 +1,24 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
3
  var _chunkETV4XYOVcjs = require('./chunk-ETV4XYOV.cjs');
4
4
 
5
5
  // src/version.ts
6
- var SDK_VERSION = true ? "1.1.0" : "0.0.0-dev";
6
+ var SDK_VERSION = true ? "1.3.0" : "0.0.0-dev";
7
+
8
+ // src/index.ts
9
+ var _money = require('@usethrottle/money'); var money = _interopRequireWildcard(_money);
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
7
22
 
8
23
  // src/errors/catalog-error.ts
9
24
  var _errors = require('@usethrottle/errors');
@@ -141,6 +156,7 @@ var ERROR_KINDS = Object.freeze({
141
156
  CUSTOMER_TOKEN_INVALID: "customerToken",
142
157
  ORIGIN_NOT_ALLOWED: "origin",
143
158
  SERVER_KEY_IN_BROWSER: "origin",
159
+ KEY_NOT_ALLOWED_IN_URL: "origin",
144
160
  NOT_FOUND: "base",
145
161
  RATE_LIMITED: "rateLimited",
146
162
  STOREFRONT_UNAVAILABLE: "base",
@@ -189,6 +205,8 @@ var ERROR_KINDS = Object.freeze({
189
205
  cart_session_not_found: "base",
190
206
  origin_not_allowed: "origin",
191
207
  checkout_unavailable: "base",
208
+ cart_owner_mismatch: "base",
209
+ cart_resolver_mismatch: "base",
192
210
  // Client codes.
193
211
  TIMEOUT: "timeout",
194
212
  NETWORK_ERROR: "network",
@@ -434,6 +452,10 @@ function resolveOptions(options) {
434
452
  const publishableKey = checkKey(options.publishableKey);
435
453
  const timeoutMs = checkTimeout(_nullishCoalesce(options.timeoutMs, () => ( DEFAULT_TIMEOUT_MS)));
436
454
  const retry = options.indexingRetry;
455
+ const keyInQuery = options.keyInQuery;
456
+ if (keyInQuery !== void 0 && typeof keyInQuery !== "boolean") {
457
+ throw optionError("INVALID_REQUEST", "keyInQuery must be a boolean.");
458
+ }
437
459
  return {
438
460
  publishableKey,
439
461
  baseUrl: checkBaseUrl(_nullishCoalesce(options.baseUrl, () => ( DEFAULT_BASE_URL))).replace(
@@ -450,7 +472,8 @@ function resolveOptions(options) {
450
472
  cache: options.cache,
451
473
  cacheScope: options.cacheScope,
452
474
  indexingMaxWaitSeconds: retry === false ? null : _nullishCoalesce(_optionalChain([retry, 'optionalAccess', _14 => _14.maxWaitSeconds]), () => ( INDEXING_MAX_WAIT_S)),
453
- headers: checkHeaders(options.headers)
475
+ headers: checkHeaders(options.headers),
476
+ keyInQuery: keyInQuery === true
454
477
  };
455
478
  }
456
479
 
@@ -516,6 +539,25 @@ function guarded(work, callerSignal, timeoutMs) {
516
539
  );
517
540
  });
518
541
  }
542
+ var KEY_VALUE = /([?&]key=)[^&\s"']*/gu;
543
+ var KEPT_DETAILS = ["name", "code", "errno", "type"];
544
+ var MAX_REDACT_DEPTH = 5;
545
+ function redactKey(cause, depth = 0) {
546
+ if (typeof cause !== "object" || cause === null) return cause;
547
+ const source = cause;
548
+ if (typeof source.message !== "string") return cause;
549
+ if (depth >= MAX_REDACT_DEPTH) return void 0;
550
+ const original = source.message;
551
+ const message = original.replace(KEY_VALUE, "$1[redacted]");
552
+ const nested = redactKey(source.cause, depth + 1);
553
+ if (message === original && nested === source.cause) return cause;
554
+ const copy = new Error(message);
555
+ for (const name of KEPT_DETAILS) {
556
+ if (source[name] !== void 0) copy[name] = source[name];
557
+ }
558
+ if (nested !== void 0) copy.cause = nested;
559
+ return copy;
560
+ }
519
561
  function send(fetchFn, url, init, signal, timeoutMs) {
520
562
  return guarded(
521
563
  async (attemptSignal) => {
@@ -526,13 +568,13 @@ function send(fetchFn, url, init, signal, timeoutMs) {
526
568
  signal: attemptSignal
527
569
  });
528
570
  } catch (cause) {
529
- throw new NetworkError({ cause });
571
+ throw new NetworkError({ cause: redactKey(cause) });
530
572
  }
531
573
  let text;
532
574
  try {
533
575
  text = await response.text();
534
576
  } catch (cause) {
535
- throw new NetworkError({ cause });
577
+ throw new NetworkError({ cause: redactKey(cause) });
536
578
  }
537
579
  return { status: response.status, headers: response.headers, text };
538
580
  },
@@ -584,6 +626,8 @@ function createTransport(o) {
584
626
  pairs.push(["currency", currency]);
585
627
  if (locale !== void 0) pairs.push(["locale", locale]);
586
628
  }
629
+ const keyInUrl = isGet && o.keyInQuery;
630
+ if (keyInUrl) pairs = [..._nullishCoalesce(pairs, () => ( [])), ["key", o.publishableKey]];
587
631
  const url = o.baseUrl + spec.path + (pairs === void 0 ? "" : queryString(pairs));
588
632
  const timeoutMs = call.timeoutMs === void 0 ? o.timeoutMs : checkTimeout(call.timeoutMs);
589
633
  if (isGet && (spec.body !== void 0 || _optionalChain([call, 'access', _24 => _24.fetchInit, 'optionalAccess', _25 => _25.body]) !== void 0)) {
@@ -591,20 +635,22 @@ function createTransport(o) {
591
635
  }
592
636
  let cacheKey;
593
637
  let cached;
594
- if (isGet && o.cache !== void 0 && spec.ifNoneMatch === void 0) {
638
+ const bypassCache = spec.tokenFree === true && keyInUrl;
639
+ if (isGet && !bypassCache && o.cache !== void 0 && spec.ifNoneMatch === void 0) {
595
640
  try {
596
- cacheKey = `${url}\0${o.cacheScope === void 0 ? "" : o.cacheScope()}`;
641
+ const scope = o.cacheScope === void 0 || spec.tokenFree === true ? "" : o.cacheScope();
642
+ cacheKey = `${url}\0${scope}`;
597
643
  cached = o.cache.get(cacheKey);
598
644
  } catch (e3) {
599
645
  cacheKey = void 0;
600
646
  }
601
647
  }
602
- const ifNoneMatch = _nullishCoalesce(spec.ifNoneMatch, () => ( _optionalChain([cached, 'optionalAccess', _26 => _26.etag])));
648
+ const ifNoneMatch = bypassCache ? void 0 : _nullishCoalesce(spec.ifNoneMatch, () => ( _optionalChain([cached, 'optionalAccess', _26 => _26.etag])));
603
649
  const headers = {
604
650
  ...o.headers,
605
- accept: "application/json",
606
- "x-catalog-key": o.publishableKey
651
+ accept: "application/json"
607
652
  };
653
+ if (!keyInUrl) headers["x-catalog-key"] = o.publishableKey;
608
654
  if (!isGet) headers["content-type"] = "application/json";
609
655
  if (ifNoneMatch !== void 0) headers["if-none-match"] = ifNoneMatch;
610
656
  if (spec.idempotencyKey !== void 0)
@@ -612,7 +658,7 @@ function createTransport(o) {
612
658
  const body = isGet ? void 0 : JSON.stringify(_nullishCoalesce(spec.body, () => ( {})));
613
659
  const source = o.customerToken;
614
660
  const tokenRead = () => source === void 0 ? Promise.resolve(null) : guarded(() => readToken(source), signal, timeoutMs);
615
- const first = spec.customerToken === "none" ? null : await tokenRead();
661
+ const first = spec.customerToken === "none" || spec.tokenFree === true ? null : await tokenRead();
616
662
  if (first === false && spec.tokenFallback === "throw") {
617
663
  throw new CustomerTokenInvalidError({
618
664
  code: "CUSTOMER_TOKEN_INVALID",
@@ -702,6 +748,9 @@ function createTransport(o) {
702
748
  function createCartSession(options, sessionId, throttleBaseUrl) {
703
749
  const base = (_nullishCoalesce(throttleBaseUrl, () => ( THROTTLE_API_URL))).replace(/\/+$/u, "");
704
750
  async function call(method, tail, body, opts, accept) {
751
+ return (await exchange(method, tail, body, opts, accept)).data;
752
+ }
753
+ async function exchange(method, tail, body, opts, accept) {
705
754
  const signal = _optionalChain([opts, 'optionalAccess', _30 => _30.signal]);
706
755
  if (_optionalChain([signal, 'optionalAccess', _31 => _31.aborted])) throw signal.reason;
707
756
  const timeoutMs = _optionalChain([opts, 'optionalAccess', _32 => _32.timeoutMs]) === void 0 ? options.timeoutMs : checkTimeout(opts.timeoutMs);
@@ -739,7 +788,10 @@ function createCartSession(options, sessionId, throttleBaseUrl) {
739
788
  retryable: false
740
789
  });
741
790
  }
742
- return data;
791
+ return {
792
+ data,
793
+ meta: parsed.meta
794
+ };
743
795
  }
744
796
  throw toCatalogError(mapInput);
745
797
  }
@@ -782,6 +834,27 @@ function createCartSession(options, sessionId, throttleBaseUrl) {
782
834
  opts,
783
835
  (data) => isHttpUrl(data.checkoutUrl)
784
836
  );
837
+ },
838
+ absorb: async (fromSessionId, opts) => {
839
+ const { data, meta } = await exchange(
840
+ "POST",
841
+ () => "/absorb",
842
+ { fromSessionId },
843
+ opts
844
+ );
845
+ if (typeof data.cart !== "object" || data.cart === null)
846
+ throw new CatalogError({
847
+ code: "INVALID_RESPONSE",
848
+ message: "Throttle answered an absorb without a cart.",
849
+ statusCode: 200,
850
+ source: "throttle",
851
+ retryable: false
852
+ });
853
+ const warnings = _optionalChain([meta, 'optionalAccess', _36 => _36.warnings]);
854
+ return {
855
+ cart: data.cart,
856
+ warnings: Array.isArray(warnings) ? warnings : []
857
+ };
785
858
  }
786
859
  };
787
860
  }
@@ -810,7 +883,7 @@ function createCarts(transport, options) {
810
883
  // The input's own currency and locale win over the call's and the
811
884
  // client's; the return URLs are sent unchanged (P6-R2).
812
885
  create: async (input, opts) => {
813
- if (_optionalChain([opts, 'optionalAccess', _36 => _36.signal, 'optionalAccess', _37 => _37.aborted])) throw opts.signal.reason;
886
+ if (_optionalChain([opts, 'optionalAccess', _37 => _37.signal, 'optionalAccess', _38 => _38.aborted])) throw opts.signal.reason;
814
887
  for (const field of [
815
888
  "returnUrl",
816
889
  "cancelUrl",
@@ -833,12 +906,12 @@ function createCarts(transport, options) {
833
906
  customerToken: "optional",
834
907
  tokenFallback: "throw",
835
908
  featureRoute: true,
836
- ..._optionalChain([opts, 'optionalAccess', _38 => _38.idempotencyKey]) === void 0 ? {} : { idempotencyKey: opts.idempotencyKey }
909
+ ..._optionalChain([opts, 'optionalAccess', _39 => _39.idempotencyKey]) === void 0 ? {} : { idempotencyKey: opts.idempotencyKey }
837
910
  },
838
911
  opts
839
912
  );
840
913
  },
841
- session: (sessionId, opts) => createCartSession(options, sessionId, _optionalChain([opts, 'optionalAccess', _39 => _39.throttleBaseUrl]))
914
+ session: (sessionId, opts) => createCartSession(options, sessionId, _optionalChain([opts, 'optionalAccess', _40 => _40.throttleBaseUrl]))
842
915
  };
843
916
  }
844
917
 
@@ -879,11 +952,11 @@ function productsQuery(query = {}) {
879
952
  featured: query.featured,
880
953
  handles: query.handles,
881
954
  skus: query.skus,
882
- year: _optionalChain([vehicle, 'optionalAccess', _40 => _40.year]),
883
- make: _optionalChain([vehicle, 'optionalAccess', _41 => _41.make]),
884
- model: _optionalChain([vehicle, 'optionalAccess', _42 => _42.model]),
885
- submodel: _optionalChain([vehicle, 'optionalAccess', _43 => _43.submodel]),
886
- engine: _optionalChain([vehicle, 'optionalAccess', _44 => _44.engine]),
955
+ year: _optionalChain([vehicle, 'optionalAccess', _41 => _41.year]),
956
+ make: _optionalChain([vehicle, 'optionalAccess', _42 => _42.make]),
957
+ model: _optionalChain([vehicle, 'optionalAccess', _43 => _43.model]),
958
+ submodel: _optionalChain([vehicle, 'optionalAccess', _44 => _44.submodel]),
959
+ engine: _optionalChain([vehicle, 'optionalAccess', _45 => _45.engine]),
887
960
  sort: query.sort,
888
961
  limit: query.limit,
889
962
  page: query.page,
@@ -921,7 +994,7 @@ function createProducts(transport) {
921
994
  path: `${SF}/products/${pathSegment(handle)}`,
922
995
  // Always the full product (P6-R18).
923
996
  query: buildQuery(
924
- { fields: "full", include: commaList(_optionalChain([opts, 'optionalAccess', _45 => _45.include])) },
997
+ { fields: "full", include: commaList(_optionalChain([opts, 'optionalAccess', _46 => _46.include])) },
925
998
  ["fields", "include"]
926
999
  ),
927
1000
  customerToken: "optional",
@@ -937,7 +1010,7 @@ function createProducts(transport) {
937
1010
  method: "GET",
938
1011
  path: `${SF}/products/${pathSegment(handle)}/related`,
939
1012
  query: buildQuery(
940
- { limit: _optionalChain([opts, 'optionalAccess', _46 => _46.limit]), fields: commaList(_optionalChain([opts, 'optionalAccess', _47 => _47.fields])) },
1013
+ { limit: _optionalChain([opts, 'optionalAccess', _47 => _47.limit]), fields: commaList(_optionalChain([opts, 'optionalAccess', _48 => _48.fields])) },
941
1014
  ["limit", "fields"]
942
1015
  ),
943
1016
  customerToken: "optional",
@@ -973,8 +1046,10 @@ function createSuggest(transport) {
973
1046
  {
974
1047
  method: "GET",
975
1048
  path: `${SF}/suggest`,
976
- query: buildQuery({ q, limit: _optionalChain([opts, 'optionalAccess', _48 => _48.limit]) }, ["q", "limit"]),
977
- customerToken: "optional",
1049
+ query: buildQuery({ q, limit: _optionalChain([opts, 'optionalAccess', _49 => _49.limit]) }, ["q", "limit"]),
1050
+ // Never a customer token (P8-R22): list prices for everyone.
1051
+ customerToken: "none",
1052
+ tokenFree: true,
978
1053
  tokenFallback: "anonymous",
979
1054
  featureRoute: true,
980
1055
  context: "priced"
@@ -996,10 +1071,10 @@ function createReviews(transport) {
996
1071
  path: reviewsPath(handle),
997
1072
  query: buildQuery(
998
1073
  {
999
- sort: _optionalChain([opts, 'optionalAccess', _49 => _49.sort]),
1000
- rating: _optionalChain([opts, 'optionalAccess', _50 => _50.rating]),
1001
- limit: _optionalChain([opts, 'optionalAccess', _51 => _51.limit]),
1002
- cursor: _optionalChain([opts, 'optionalAccess', _52 => _52.cursor])
1074
+ sort: _optionalChain([opts, 'optionalAccess', _50 => _50.sort]),
1075
+ rating: _optionalChain([opts, 'optionalAccess', _51 => _51.rating]),
1076
+ limit: _optionalChain([opts, 'optionalAccess', _52 => _52.limit]),
1077
+ cursor: _optionalChain([opts, 'optionalAccess', _53 => _53.cursor])
1003
1078
  },
1004
1079
  ["sort", "rating", "limit", "cursor"]
1005
1080
  ),
@@ -1031,7 +1106,7 @@ function createReviews(transport) {
1031
1106
  customerToken: "required",
1032
1107
  tokenFallback: "throw",
1033
1108
  featureRoute: false,
1034
- ..._optionalChain([opts, 'optionalAccess', _53 => _53.idempotencyKey]) === void 0 ? {} : { idempotencyKey: opts.idempotencyKey }
1109
+ ..._optionalChain([opts, 'optionalAccess', _54 => _54.idempotencyKey]) === void 0 ? {} : { idempotencyKey: opts.idempotencyKey }
1035
1110
  },
1036
1111
  opts
1037
1112
  )
@@ -1047,7 +1122,7 @@ function createCategories(transport) {
1047
1122
  {
1048
1123
  method: "GET",
1049
1124
  path: `${SF2}/categories`,
1050
- query: buildQuery({ depth: _optionalChain([opts, 'optionalAccess', _54 => _54.depth]) }, ["depth"]),
1125
+ query: buildQuery({ depth: _optionalChain([opts, 'optionalAccess', _55 => _55.depth]) }, ["depth"]),
1051
1126
  customerToken: "none",
1052
1127
  tokenFallback: "anonymous",
1053
1128
  featureRoute: true,
@@ -1076,7 +1151,7 @@ function createTags(transport) {
1076
1151
  {
1077
1152
  method: "GET",
1078
1153
  path: `${SF2}/tags`,
1079
- query: buildQuery({ q: _optionalChain([opts, 'optionalAccess', _55 => _55.q]), limit: _optionalChain([opts, 'optionalAccess', _56 => _56.limit]) }, ["q", "limit"]),
1154
+ query: buildQuery({ q: _optionalChain([opts, 'optionalAccess', _56 => _56.q]), limit: _optionalChain([opts, 'optionalAccess', _57 => _57.limit]) }, ["q", "limit"]),
1080
1155
  customerToken: "none",
1081
1156
  tokenFallback: "anonymous",
1082
1157
  featureRoute: true
@@ -1092,7 +1167,7 @@ function createCollections(transport) {
1092
1167
  {
1093
1168
  method: "GET",
1094
1169
  path: `${SF2}/collections`,
1095
- query: buildQuery({ limit: _optionalChain([opts, 'optionalAccess', _57 => _57.limit]), cursor: _optionalChain([opts, 'optionalAccess', _58 => _58.cursor]) }, [
1170
+ query: buildQuery({ limit: _optionalChain([opts, 'optionalAccess', _58 => _58.limit]), cursor: _optionalChain([opts, 'optionalAccess', _59 => _59.cursor]) }, [
1096
1171
  "limit",
1097
1172
  "cursor"
1098
1173
  ]),
@@ -1178,4 +1253,15 @@ function createCatalogClient(options) {
1178
1253
 
1179
1254
 
1180
1255
 
1181
- exports.SDK_VERSION = SDK_VERSION; exports.CatalogError = CatalogError; exports.RateLimitedError = RateLimitedError; exports.OriginNotAllowedError = OriginNotAllowedError; exports.CustomerTokenInvalidError = CustomerTokenInvalidError; exports.CartLineError = CartLineError; exports.TimeoutError = TimeoutError; exports.NetworkError = NetworkError; exports.FeatureUnavailableError = FeatureUnavailableError; exports.isCatalogError = isCatalogError; exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL; exports.THROTTLE_CHECKOUT_URL = THROTTLE_CHECKOUT_URL; exports.RATE_LIMIT_DEFAULT_RETRY_S = RATE_LIMIT_DEFAULT_RETRY_S; exports.STALE_MS = STALE_MS; exports.CACHE_MAX_ENTRIES = CACHE_MAX_ENTRIES; exports.SUGGEST_DEBOUNCE_MS = SUGGEST_DEBOUNCE_MS; exports.SUGGEST_MIN_CHARS = SUGGEST_MIN_CHARS; exports.INDEXING_REVALIDATE_MS = INDEXING_REVALIDATE_MS; exports.CACHE_TAG = CACHE_TAG; exports.TAG_MAX_CHARS = TAG_MAX_CHARS; exports.DEFAULT_REVALIDATE_S = DEFAULT_REVALIDATE_S; exports.LIST_PAGE_SIZE = LIST_PAGE_SIZE; exports.LIST_MAX_HANDLES = LIST_MAX_HANDLES; exports.CATEGORY_TREE_MAX_DEPTH = CATEGORY_TREE_MAX_DEPTH; exports.createCatalogClient = createCatalogClient;
1256
+
1257
+
1258
+
1259
+
1260
+
1261
+
1262
+
1263
+
1264
+
1265
+
1266
+
1267
+ exports.SDK_VERSION = SDK_VERSION; exports.CatalogError = CatalogError; exports.RateLimitedError = RateLimitedError; exports.OriginNotAllowedError = OriginNotAllowedError; exports.CustomerTokenInvalidError = CustomerTokenInvalidError; exports.CartLineError = CartLineError; exports.TimeoutError = TimeoutError; exports.NetworkError = NetworkError; exports.FeatureUnavailableError = FeatureUnavailableError; exports.isCatalogError = isCatalogError; exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL; exports.THROTTLE_CHECKOUT_URL = THROTTLE_CHECKOUT_URL; exports.RATE_LIMIT_DEFAULT_RETRY_S = RATE_LIMIT_DEFAULT_RETRY_S; exports.STALE_MS = STALE_MS; exports.CACHE_MAX_ENTRIES = CACHE_MAX_ENTRIES; exports.SUGGEST_DEBOUNCE_MS = SUGGEST_DEBOUNCE_MS; exports.SUGGEST_MIN_CHARS = SUGGEST_MIN_CHARS; exports.INDEXING_REVALIDATE_MS = INDEXING_REVALIDATE_MS; exports.CACHE_TAG = CACHE_TAG; exports.TAG_MAX_CHARS = TAG_MAX_CHARS; exports.DEFAULT_REVALIDATE_S = DEFAULT_REVALIDATE_S; exports.LIST_PAGE_SIZE = LIST_PAGE_SIZE; exports.LIST_MAX_HANDLES = LIST_MAX_HANDLES; exports.CATEGORY_TREE_MAX_DEPTH = CATEGORY_TREE_MAX_DEPTH; exports.createCatalogClient = createCatalogClient; exports.money = money; exports.CURRENCIES = _money.CURRENCIES; exports.UnknownCurrencyError = _money.UnknownCurrencyError; exports.currencySymbol = _money.currencySymbol; exports.formatMoney = _money.formatMoney; exports.isKnownCurrency = _money.isKnownCurrency; exports.minorUnitsOf = _money.minorUnitsOf; exports.moneyValue = _money.moneyValue; exports.parseMoney = _money.parseMoney; exports.toDecimalString = _money.toDecimalString; exports.toMinor = _money.toMinor;
@@ -3,7 +3,22 @@ import {
3
3
  } from "./chunk-PKBMQBKP.js";
4
4
 
5
5
  // src/version.ts
6
- var SDK_VERSION = true ? "1.1.0" : "0.0.0-dev";
6
+ var SDK_VERSION = true ? "1.3.0" : "0.0.0-dev";
7
+
8
+ // src/index.ts
9
+ import * as money from "@usethrottle/money";
10
+ import {
11
+ CURRENCIES,
12
+ UnknownCurrencyError,
13
+ currencySymbol,
14
+ formatMoney,
15
+ isKnownCurrency,
16
+ minorUnitsOf,
17
+ moneyValue,
18
+ parseMoney,
19
+ toDecimalString,
20
+ toMinor
21
+ } from "@usethrottle/money";
7
22
 
8
23
  // src/errors/catalog-error.ts
9
24
  import { ThrottleError } from "@usethrottle/errors";
@@ -141,6 +156,7 @@ var ERROR_KINDS = Object.freeze({
141
156
  CUSTOMER_TOKEN_INVALID: "customerToken",
142
157
  ORIGIN_NOT_ALLOWED: "origin",
143
158
  SERVER_KEY_IN_BROWSER: "origin",
159
+ KEY_NOT_ALLOWED_IN_URL: "origin",
144
160
  NOT_FOUND: "base",
145
161
  RATE_LIMITED: "rateLimited",
146
162
  STOREFRONT_UNAVAILABLE: "base",
@@ -189,6 +205,8 @@ var ERROR_KINDS = Object.freeze({
189
205
  cart_session_not_found: "base",
190
206
  origin_not_allowed: "origin",
191
207
  checkout_unavailable: "base",
208
+ cart_owner_mismatch: "base",
209
+ cart_resolver_mismatch: "base",
192
210
  // Client codes.
193
211
  TIMEOUT: "timeout",
194
212
  NETWORK_ERROR: "network",
@@ -434,6 +452,10 @@ function resolveOptions(options) {
434
452
  const publishableKey = checkKey(options.publishableKey);
435
453
  const timeoutMs = checkTimeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
436
454
  const retry = options.indexingRetry;
455
+ const keyInQuery = options.keyInQuery;
456
+ if (keyInQuery !== void 0 && typeof keyInQuery !== "boolean") {
457
+ throw optionError("INVALID_REQUEST", "keyInQuery must be a boolean.");
458
+ }
437
459
  return {
438
460
  publishableKey,
439
461
  baseUrl: checkBaseUrl(options.baseUrl ?? DEFAULT_BASE_URL).replace(
@@ -450,7 +472,8 @@ function resolveOptions(options) {
450
472
  cache: options.cache,
451
473
  cacheScope: options.cacheScope,
452
474
  indexingMaxWaitSeconds: retry === false ? null : retry?.maxWaitSeconds ?? INDEXING_MAX_WAIT_S,
453
- headers: checkHeaders(options.headers)
475
+ headers: checkHeaders(options.headers),
476
+ keyInQuery: keyInQuery === true
454
477
  };
455
478
  }
456
479
 
@@ -516,6 +539,25 @@ function guarded(work, callerSignal, timeoutMs) {
516
539
  );
517
540
  });
518
541
  }
542
+ var KEY_VALUE = /([?&]key=)[^&\s"']*/gu;
543
+ var KEPT_DETAILS = ["name", "code", "errno", "type"];
544
+ var MAX_REDACT_DEPTH = 5;
545
+ function redactKey(cause, depth = 0) {
546
+ if (typeof cause !== "object" || cause === null) return cause;
547
+ const source = cause;
548
+ if (typeof source.message !== "string") return cause;
549
+ if (depth >= MAX_REDACT_DEPTH) return void 0;
550
+ const original = source.message;
551
+ const message = original.replace(KEY_VALUE, "$1[redacted]");
552
+ const nested = redactKey(source.cause, depth + 1);
553
+ if (message === original && nested === source.cause) return cause;
554
+ const copy = new Error(message);
555
+ for (const name of KEPT_DETAILS) {
556
+ if (source[name] !== void 0) copy[name] = source[name];
557
+ }
558
+ if (nested !== void 0) copy.cause = nested;
559
+ return copy;
560
+ }
519
561
  function send(fetchFn, url, init, signal, timeoutMs) {
520
562
  return guarded(
521
563
  async (attemptSignal) => {
@@ -526,13 +568,13 @@ function send(fetchFn, url, init, signal, timeoutMs) {
526
568
  signal: attemptSignal
527
569
  });
528
570
  } catch (cause) {
529
- throw new NetworkError({ cause });
571
+ throw new NetworkError({ cause: redactKey(cause) });
530
572
  }
531
573
  let text;
532
574
  try {
533
575
  text = await response.text();
534
576
  } catch (cause) {
535
- throw new NetworkError({ cause });
577
+ throw new NetworkError({ cause: redactKey(cause) });
536
578
  }
537
579
  return { status: response.status, headers: response.headers, text };
538
580
  },
@@ -584,6 +626,8 @@ function createTransport(o) {
584
626
  pairs.push(["currency", currency]);
585
627
  if (locale !== void 0) pairs.push(["locale", locale]);
586
628
  }
629
+ const keyInUrl = isGet && o.keyInQuery;
630
+ if (keyInUrl) pairs = [...pairs ?? [], ["key", o.publishableKey]];
587
631
  const url = o.baseUrl + spec.path + (pairs === void 0 ? "" : queryString(pairs));
588
632
  const timeoutMs = call.timeoutMs === void 0 ? o.timeoutMs : checkTimeout(call.timeoutMs);
589
633
  if (isGet && (spec.body !== void 0 || call.fetchInit?.body !== void 0)) {
@@ -591,20 +635,22 @@ function createTransport(o) {
591
635
  }
592
636
  let cacheKey;
593
637
  let cached;
594
- if (isGet && o.cache !== void 0 && spec.ifNoneMatch === void 0) {
638
+ const bypassCache = spec.tokenFree === true && keyInUrl;
639
+ if (isGet && !bypassCache && o.cache !== void 0 && spec.ifNoneMatch === void 0) {
595
640
  try {
596
- cacheKey = `${url}\0${o.cacheScope === void 0 ? "" : o.cacheScope()}`;
641
+ const scope = o.cacheScope === void 0 || spec.tokenFree === true ? "" : o.cacheScope();
642
+ cacheKey = `${url}\0${scope}`;
597
643
  cached = o.cache.get(cacheKey);
598
644
  } catch {
599
645
  cacheKey = void 0;
600
646
  }
601
647
  }
602
- const ifNoneMatch = spec.ifNoneMatch ?? cached?.etag;
648
+ const ifNoneMatch = bypassCache ? void 0 : spec.ifNoneMatch ?? cached?.etag;
603
649
  const headers = {
604
650
  ...o.headers,
605
- accept: "application/json",
606
- "x-catalog-key": o.publishableKey
651
+ accept: "application/json"
607
652
  };
653
+ if (!keyInUrl) headers["x-catalog-key"] = o.publishableKey;
608
654
  if (!isGet) headers["content-type"] = "application/json";
609
655
  if (ifNoneMatch !== void 0) headers["if-none-match"] = ifNoneMatch;
610
656
  if (spec.idempotencyKey !== void 0)
@@ -612,7 +658,7 @@ function createTransport(o) {
612
658
  const body = isGet ? void 0 : JSON.stringify(spec.body ?? {});
613
659
  const source = o.customerToken;
614
660
  const tokenRead = () => source === void 0 ? Promise.resolve(null) : guarded(() => readToken(source), signal, timeoutMs);
615
- const first = spec.customerToken === "none" ? null : await tokenRead();
661
+ const first = spec.customerToken === "none" || spec.tokenFree === true ? null : await tokenRead();
616
662
  if (first === false && spec.tokenFallback === "throw") {
617
663
  throw new CustomerTokenInvalidError({
618
664
  code: "CUSTOMER_TOKEN_INVALID",
@@ -702,6 +748,9 @@ function createTransport(o) {
702
748
  function createCartSession(options, sessionId, throttleBaseUrl) {
703
749
  const base = (throttleBaseUrl ?? THROTTLE_API_URL).replace(/\/+$/u, "");
704
750
  async function call(method, tail, body, opts, accept) {
751
+ return (await exchange(method, tail, body, opts, accept)).data;
752
+ }
753
+ async function exchange(method, tail, body, opts, accept) {
705
754
  const signal = opts?.signal;
706
755
  if (signal?.aborted) throw signal.reason;
707
756
  const timeoutMs = opts?.timeoutMs === void 0 ? options.timeoutMs : checkTimeout(opts.timeoutMs);
@@ -739,7 +788,10 @@ function createCartSession(options, sessionId, throttleBaseUrl) {
739
788
  retryable: false
740
789
  });
741
790
  }
742
- return data;
791
+ return {
792
+ data,
793
+ meta: parsed.meta
794
+ };
743
795
  }
744
796
  throw toCatalogError(mapInput);
745
797
  }
@@ -782,6 +834,27 @@ function createCartSession(options, sessionId, throttleBaseUrl) {
782
834
  opts,
783
835
  (data) => isHttpUrl(data.checkoutUrl)
784
836
  );
837
+ },
838
+ absorb: async (fromSessionId, opts) => {
839
+ const { data, meta } = await exchange(
840
+ "POST",
841
+ () => "/absorb",
842
+ { fromSessionId },
843
+ opts
844
+ );
845
+ if (typeof data.cart !== "object" || data.cart === null)
846
+ throw new CatalogError({
847
+ code: "INVALID_RESPONSE",
848
+ message: "Throttle answered an absorb without a cart.",
849
+ statusCode: 200,
850
+ source: "throttle",
851
+ retryable: false
852
+ });
853
+ const warnings = meta?.warnings;
854
+ return {
855
+ cart: data.cart,
856
+ warnings: Array.isArray(warnings) ? warnings : []
857
+ };
785
858
  }
786
859
  };
787
860
  }
@@ -974,7 +1047,9 @@ function createSuggest(transport) {
974
1047
  method: "GET",
975
1048
  path: `${SF}/suggest`,
976
1049
  query: buildQuery({ q, limit: opts?.limit }, ["q", "limit"]),
977
- customerToken: "optional",
1050
+ // Never a customer token (P8-R22): list prices for everyone.
1051
+ customerToken: "none",
1052
+ tokenFree: true,
978
1053
  tokenFallback: "anonymous",
979
1054
  featureRoute: true,
980
1055
  context: "priced"
@@ -1177,5 +1252,16 @@ export {
1177
1252
  LIST_PAGE_SIZE,
1178
1253
  LIST_MAX_HANDLES,
1179
1254
  CATEGORY_TREE_MAX_DEPTH,
1180
- createCatalogClient
1255
+ createCatalogClient,
1256
+ money,
1257
+ CURRENCIES,
1258
+ UnknownCurrencyError,
1259
+ currencySymbol,
1260
+ formatMoney,
1261
+ isKnownCurrency,
1262
+ minorUnitsOf,
1263
+ moneyValue,
1264
+ parseMoney,
1265
+ toDecimalString,
1266
+ toMinor
1181
1267
  };