@viu/emporix-sdk 2.13.1 → 2.14.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 (96) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +1 -1
  3. package/dist/availability.cjs +3 -1
  4. package/dist/availability.cjs.map +1 -1
  5. package/dist/availability.d.cts +1 -1
  6. package/dist/availability.d.ts +1 -1
  7. package/dist/availability.js +2 -2
  8. package/dist/cart.cjs.map +1 -1
  9. package/dist/cart.d.cts +1 -1
  10. package/dist/cart.d.ts +1 -1
  11. package/dist/cart.js +2 -2
  12. package/dist/category.cjs +3 -1
  13. package/dist/category.cjs.map +1 -1
  14. package/dist/category.d.cts +1 -1
  15. package/dist/category.d.ts +1 -1
  16. package/dist/category.js +1 -1
  17. package/dist/checkout.cjs.map +1 -1
  18. package/dist/checkout.d.cts +1 -1
  19. package/dist/checkout.d.ts +1 -1
  20. package/dist/checkout.js +2 -2
  21. package/dist/{chunk-BFQ732VZ.js → chunk-3BDDYX3V.js} +3 -3
  22. package/dist/{chunk-BFQ732VZ.js.map → chunk-3BDDYX3V.js.map} +1 -1
  23. package/dist/{chunk-BK25FDTH.js → chunk-BDFOD7W3.js} +9 -5
  24. package/dist/chunk-BDFOD7W3.js.map +1 -0
  25. package/dist/{chunk-XMEP74BU.js → chunk-BPVMQFO4.js} +6 -4
  26. package/dist/chunk-BPVMQFO4.js.map +1 -0
  27. package/dist/{chunk-Z6PNI5VT.js → chunk-CR2JRH7T.js} +3 -3
  28. package/dist/{chunk-Z6PNI5VT.js.map → chunk-CR2JRH7T.js.map} +1 -1
  29. package/dist/{chunk-HKPZI7Y5.js → chunk-EINAVCV2.js} +3 -3
  30. package/dist/{chunk-HKPZI7Y5.js.map → chunk-EINAVCV2.js.map} +1 -1
  31. package/dist/{chunk-WSXRUYBE.js → chunk-FU5WQALW.js} +3 -3
  32. package/dist/{chunk-WSXRUYBE.js.map → chunk-FU5WQALW.js.map} +1 -1
  33. package/dist/{chunk-B4UOYJKW.js → chunk-MIUVJEGR.js} +8 -4
  34. package/dist/chunk-MIUVJEGR.js.map +1 -0
  35. package/dist/{chunk-I3YYGSN2.js → chunk-P3UHYMDH.js} +3 -3
  36. package/dist/{chunk-I3YYGSN2.js.map → chunk-P3UHYMDH.js.map} +1 -1
  37. package/dist/{chunk-TLH6SLAF.js → chunk-UOBOFCFU.js} +27 -34
  38. package/dist/chunk-UOBOFCFU.js.map +1 -0
  39. package/dist/{chunk-KTODT6DV.js → chunk-UWGGF4MC.js} +7 -3
  40. package/dist/chunk-UWGGF4MC.js.map +1 -0
  41. package/dist/{chunk-AUVJ5V47.js → chunk-WWCCB22A.js} +5 -3
  42. package/dist/chunk-WWCCB22A.js.map +1 -0
  43. package/dist/{chunk-OMYVOOSX.js → chunk-XG2TKFY5.js} +3 -3
  44. package/dist/{chunk-OMYVOOSX.js.map → chunk-XG2TKFY5.js.map} +1 -1
  45. package/dist/companies.d.cts +1 -1
  46. package/dist/companies.d.ts +1 -1
  47. package/dist/contacts.d.cts +1 -1
  48. package/dist/contacts.d.ts +1 -1
  49. package/dist/{context-CD7F_LHT.d.cts → context-DhoFgoKf.d.cts} +13 -0
  50. package/dist/{context-CD7F_LHT.d.ts → context-DhoFgoKf.d.ts} +13 -0
  51. package/dist/customer-groups.d.cts +1 -1
  52. package/dist/customer-groups.d.ts +1 -1
  53. package/dist/customer.cjs +23 -31
  54. package/dist/customer.cjs.map +1 -1
  55. package/dist/customer.d.cts +1 -1
  56. package/dist/customer.d.ts +1 -1
  57. package/dist/customer.js +3 -3
  58. package/dist/index.cjs +114 -50
  59. package/dist/index.cjs.map +1 -1
  60. package/dist/index.d.cts +9 -3
  61. package/dist/index.d.ts +9 -3
  62. package/dist/index.js +90 -36
  63. package/dist/index.js.map +1 -1
  64. package/dist/locations.d.cts +1 -1
  65. package/dist/locations.d.ts +1 -1
  66. package/dist/media.cjs.map +1 -1
  67. package/dist/media.d.cts +1 -1
  68. package/dist/media.d.ts +1 -1
  69. package/dist/media.js +2 -2
  70. package/dist/orders.d.cts +1 -1
  71. package/dist/orders.d.ts +1 -1
  72. package/dist/payment.cjs.map +1 -1
  73. package/dist/payment.d.cts +1 -1
  74. package/dist/payment.d.ts +1 -1
  75. package/dist/payment.js +3 -3
  76. package/dist/price.cjs +6 -2
  77. package/dist/price.cjs.map +1 -1
  78. package/dist/price.d.cts +1 -1
  79. package/dist/price.d.ts +1 -1
  80. package/dist/price.js +2 -2
  81. package/dist/product.cjs +6 -2
  82. package/dist/product.cjs.map +1 -1
  83. package/dist/product.d.cts +1 -1
  84. package/dist/product.d.ts +1 -1
  85. package/dist/product.js +1 -1
  86. package/dist/segment.cjs.map +1 -1
  87. package/dist/segment.d.cts +1 -1
  88. package/dist/segment.d.ts +1 -1
  89. package/dist/segment.js +3 -3
  90. package/package.json +1 -1
  91. package/dist/chunk-AUVJ5V47.js.map +0 -1
  92. package/dist/chunk-B4UOYJKW.js.map +0 -1
  93. package/dist/chunk-BK25FDTH.js.map +0 -1
  94. package/dist/chunk-KTODT6DV.js.map +0 -1
  95. package/dist/chunk-TLH6SLAF.js.map +0 -1
  96. package/dist/chunk-XMEP74BU.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -48,6 +48,10 @@ var EmporixValidationError = class extends EmporixError {
48
48
  };
49
49
  var EmporixServerError = class extends EmporixError {
50
50
  };
51
+ var EmporixTimeoutError = class extends EmporixError {
52
+ };
53
+ var EmporixNetworkError = class extends EmporixError {
54
+ };
51
55
  var EmporixInsufficientScopeError = class extends EmporixForbiddenError {
52
56
  requiredScope;
53
57
  constructor(message, status, body, requiredScope) {
@@ -457,6 +461,26 @@ var DefaultTokenProvider = class {
457
461
  this.serviceLocks.set(set, p);
458
462
  return p;
459
463
  }
464
+ /**
465
+ * fetch with the configured read timeout. Token endpoints sit in front of
466
+ * single-flight locks — one hung call would otherwise block every request
467
+ * on this credential set forever.
468
+ */
469
+ async boundedFetch(url, init = {}) {
470
+ const ms = this.cfg.timeouts?.readMs ?? 6e4;
471
+ const controller = new AbortController();
472
+ const timer = setTimeout(() => controller.abort(), ms);
473
+ try {
474
+ return await fetch(url, { ...init, signal: controller.signal });
475
+ } catch (err) {
476
+ if (err.name === "AbortError") {
477
+ throw new EmporixTimeoutError(`token request timed out after ${ms}ms`);
478
+ }
479
+ throw new EmporixNetworkError(`token request network failure: ${err.message}`);
480
+ } finally {
481
+ clearTimeout(timer);
482
+ }
483
+ }
460
484
  async requestServiceToken(set) {
461
485
  const c = this.creds(set);
462
486
  const body = new URLSearchParams({
@@ -465,7 +489,7 @@ var DefaultTokenProvider = class {
465
489
  client_secret: c.secret
466
490
  });
467
491
  if (c.scope) body.set("scope", c.scope);
468
- const res = await fetch(`${this.cfg.host}/oauth/token`, {
492
+ const res = await this.boundedFetch(`${this.cfg.host}/oauth/token`, {
469
493
  method: "POST",
470
494
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
471
495
  body
@@ -538,7 +562,7 @@ var DefaultTokenProvider = class {
538
562
  url.searchParams.set("refresh_token", this.anon.refreshToken);
539
563
  }
540
564
  try {
541
- const res = await fetch(url, { method: "GET" });
565
+ const res = await this.boundedFetch(url, { method: "GET" });
542
566
  const json = await res.json().catch(() => ({}));
543
567
  if (!res.ok) {
544
568
  this.notifyRefresh("anonymous", false);
@@ -614,10 +638,18 @@ var HttpClient = class {
614
638
  attempt
615
639
  });
616
640
  const controller = new AbortController();
617
- const timer = setTimeout(
618
- () => controller.abort(),
619
- o.timeoutMs ?? this.opts.timeouts.readMs
620
- );
641
+ const overallMs = o.timeoutMs ?? this.opts.timeouts.readMs;
642
+ const connectMs = Math.min(this.opts.timeouts.connectMs, overallMs);
643
+ const timeoutMsg = `${o.method} ${o.path} timed out after ${overallMs}ms (connect budget ${connectMs}ms)`;
644
+ let connectTimer;
645
+ let overallTimer;
646
+ const overallBudget = new Promise((_, reject) => {
647
+ overallTimer = setTimeout(() => {
648
+ controller.abort();
649
+ reject(new EmporixTimeoutError(timeoutMsg));
650
+ }, overallMs);
651
+ });
652
+ connectTimer = setTimeout(() => controller.abort(), connectMs);
621
653
  const isFormData = typeof FormData !== "undefined" && o.body instanceof FormData;
622
654
  const init = {
623
655
  method: o.method,
@@ -628,12 +660,27 @@ var HttpClient = class {
628
660
  init.body = isFormData ? o.body : JSON.stringify(o.body);
629
661
  }
630
662
  let res;
663
+ let text;
631
664
  try {
632
- res = await fetch(url, init);
665
+ res = await Promise.race([fetch(url, init), overallBudget]);
666
+ clearTimeout(connectTimer);
667
+ connectTimer = void 0;
668
+ const body = res.text();
669
+ body.catch(() => {
670
+ });
671
+ text = await Promise.race([body, overallBudget]);
672
+ } catch (err) {
673
+ if (err instanceof EmporixTimeoutError) throw err;
674
+ if (err.name === "AbortError") {
675
+ throw new EmporixTimeoutError(timeoutMsg);
676
+ }
677
+ throw new EmporixNetworkError(
678
+ `${o.method} ${o.path} network failure: ${err.message}`
679
+ );
633
680
  } finally {
634
- clearTimeout(timer);
681
+ if (connectTimer !== void 0) clearTimeout(connectTimer);
682
+ if (overallTimer !== void 0) clearTimeout(overallTimer);
635
683
  }
636
- const text = await res.text();
637
684
  const parsed = text ? safeJson(text) : void 0;
638
685
  if (res.ok) {
639
686
  log.debug("http ok", { status: res.status });
@@ -663,10 +710,12 @@ var HttpClient = class {
663
710
  }
664
711
  throw errorFromResponse(res.status, `${o.method} ${o.path} \u2192 401`, parsed);
665
712
  }
666
- const retryable = res.status >= 500 || res.status === 429;
713
+ const idempotent = o.method === "GET" || o.method === "PUT" || o.method === "DELETE" || o.idempotent === true;
714
+ const retryable = idempotent && (res.status >= 500 || res.status === 429);
667
715
  if (retryable && attempt < maxAttempts) {
668
- const retryAfter = Number(res.headers.get("Retry-After"));
669
- const backoff = Number.isFinite(retryAfter) && retryAfter >= 0 ? retryAfter * 1e3 : Math.min(1e3 * 2 ** (attempt - 1), 8e3) + Math.random() * 100;
716
+ const retryAfterHeader = res.headers.get("Retry-After");
717
+ const retryAfter = retryAfterHeader ? Number(retryAfterHeader) : Number.NaN;
718
+ const backoff = Number.isFinite(retryAfter) && retryAfter >= 0 ? Math.min(retryAfter * 1e3, 8e3) : Math.min(1e3 * 2 ** (attempt - 1), 8e3) + Math.random() * 100;
670
719
  log.warn("retryable failure", { status: res.status, attempt, backoffMs: backoff });
671
720
  await this.sleep(backoff);
672
721
  continue;
@@ -718,6 +767,15 @@ var HttpClient = class {
718
767
  }
719
768
  try {
720
769
  return await fetch(url, init);
770
+ } catch (err) {
771
+ if (err.name === "AbortError") {
772
+ throw new EmporixTimeoutError(
773
+ `${o.method} ${o.path} timed out after ${o.timeoutMs ?? this.opts.timeouts.readMs}ms`
774
+ );
775
+ }
776
+ throw new EmporixNetworkError(
777
+ `${o.method} ${o.path} network failure: ${err.message}`
778
+ );
721
779
  } finally {
722
780
  clearTimeout(timer);
723
781
  }
@@ -731,6 +789,21 @@ function requireCustomer(auth2) {
731
789
  }
732
790
 
733
791
  // src/services/customer.ts
792
+ function toSession(endpoint, wire, opts = {}) {
793
+ const customerToken = wire.access_token ?? wire.accessToken;
794
+ if (!customerToken) {
795
+ throw new EmporixAuthError(`${endpoint}: response missing access_token`, void 0, wire);
796
+ }
797
+ return {
798
+ customerToken,
799
+ saasToken: wire.saas_token ?? wire.saasToken ?? opts.carrySaasToken ?? "",
800
+ refreshToken: wire.refresh_token ?? wire.refreshToken ?? "",
801
+ sessionId: wire.session_id,
802
+ expiresIn: wire.expires_in != null ? Number(wire.expires_in) : void 0,
803
+ ...wire.social_access_token ? { socialAccessToken: wire.social_access_token } : {},
804
+ ...wire.social_id_token ? { socialIdToken: wire.social_id_token } : {}
805
+ };
806
+ }
734
807
  var CustomerService = class {
735
808
  constructor(ctx) {
736
809
  this.ctx = ctx;
@@ -762,13 +835,7 @@ var CustomerService = class {
762
835
  auth: effective,
763
836
  body: creds
764
837
  });
765
- return {
766
- customerToken: wire.access_token ?? wire.accessToken ?? "",
767
- saasToken: wire.saas_token ?? wire.saasToken ?? "",
768
- refreshToken: wire.refresh_token ?? wire.refreshToken ?? "",
769
- sessionId: wire.session_id,
770
- expiresIn: wire.expires_in
771
- };
838
+ return toSession("login", wire);
772
839
  }
773
840
  /**
774
841
  * Refreshes an authenticated customer session via its refresh token,
@@ -788,14 +855,11 @@ var CustomerService = class {
788
855
  query,
789
856
  auth: auth2
790
857
  });
791
- return {
792
- customerToken: wire.access_token ?? wire.accessToken ?? "",
793
- // Refresh does not return a saas_token — carry the original forward.
794
- saasToken: input.saasToken ?? "",
795
- refreshToken: wire.refresh_token ?? wire.refreshToken ?? "",
796
- sessionId: wire.session_id,
797
- expiresIn: wire.expires_in
798
- };
858
+ return toSession(
859
+ "refresh",
860
+ wire,
861
+ input.saasToken !== void 0 ? { carrySaasToken: input.saasToken } : {}
862
+ );
799
863
  }
800
864
  /**
801
865
  * Logs the customer out server-side: `GET /customer/{tenant}/logout?
@@ -834,15 +898,7 @@ var CustomerService = class {
834
898
  auth: auth2,
835
899
  ...input.sessionId ? { headers: { "session-id": input.sessionId } } : {}
836
900
  });
837
- return {
838
- customerToken: wire.access_token ?? wire.accessToken ?? "",
839
- saasToken: wire.saas_token ?? wire.saasToken ?? "",
840
- refreshToken: wire.refresh_token ?? wire.refreshToken ?? "",
841
- sessionId: wire.session_id,
842
- expiresIn: wire.expires_in != null ? Number(wire.expires_in) : void 0,
843
- ...wire.social_access_token ? { socialAccessToken: wire.social_access_token } : {},
844
- ...wire.social_id_token ? { socialIdToken: wire.social_id_token } : {}
845
- };
901
+ return toSession("socialLogin", wire);
846
902
  }
847
903
  /**
848
904
  * RFC 8693 token exchange: exchanges an external IdP JWT for an Emporix
@@ -861,13 +917,7 @@ var CustomerService = class {
861
917
  query,
862
918
  auth: auth2
863
919
  });
864
- return {
865
- customerToken: wire.access_token ?? wire.accessToken ?? "",
866
- saasToken: wire.saas_token ?? wire.saasToken ?? "",
867
- refreshToken: wire.refresh_token ?? wire.refreshToken ?? "",
868
- sessionId: wire.session_id,
869
- expiresIn: wire.expires_in != null ? Number(wire.expires_in) : void 0
870
- };
920
+ return toSession("exchangeToken", wire);
871
921
  }
872
922
  /** Returns the authenticated customer. Requires customer/raw auth. */
873
923
  async me(auth2) {
@@ -1037,7 +1087,9 @@ var ProductService = class {
1037
1087
  path: `/product/${this.ctx.tenant}/products/search`,
1038
1088
  query: { pageSize: chunk.length },
1039
1089
  auth: auth2,
1040
- body: { q: `id:(${chunk.join(",")})` }
1090
+ body: { q: `id:(${chunk.join(",")})` },
1091
+ idempotent: true
1092
+ // pure read over POST — safe to replay on 5xx/429
1041
1093
  })
1042
1094
  )
1043
1095
  );
@@ -1077,7 +1129,9 @@ var ProductService = class {
1077
1129
  path: `/product/${this.ctx.tenant}/products/search`,
1078
1130
  query: { pageSize: chunk.length },
1079
1131
  auth: auth2,
1080
- body: { q: `code:(${chunk.join(",")})` }
1132
+ body: { q: `code:(${chunk.join(",")})` },
1133
+ idempotent: true
1134
+ // pure read over POST — safe to replay on 5xx/429
1081
1135
  })
1082
1136
  )
1083
1137
  );
@@ -1198,7 +1252,9 @@ var CategoryService = class {
1198
1252
  path: `/product/${this.ctx.tenant}/products/search`,
1199
1253
  query: { pageSize: productIds.length },
1200
1254
  auth: auth2,
1201
- body: { q: `id:(${productIds.join(",")})` }
1255
+ body: { q: `id:(${productIds.join(",")})` },
1256
+ idempotent: true
1257
+ // pure read over POST — safe to replay on 5xx/429
1202
1258
  });
1203
1259
  return { items, pageNumber, pageSize, hasNextPage };
1204
1260
  }
@@ -1514,7 +1570,9 @@ var PriceService = class {
1514
1570
  method: "POST",
1515
1571
  path: `/price/${this.ctx.tenant}/match-prices-by-context`,
1516
1572
  auth: requireContextAuth(auth2),
1517
- body: input
1573
+ body: input,
1574
+ idempotent: true
1575
+ // pure read over POST — safe to replay on 5xx/429
1518
1576
  });
1519
1577
  return rows.map(normalizeMatch);
1520
1578
  }
@@ -1527,7 +1585,9 @@ var PriceService = class {
1527
1585
  method: "POST",
1528
1586
  path: `/price/${this.ctx.tenant}/match-prices`,
1529
1587
  auth: auth2,
1530
- body: input
1588
+ body: input,
1589
+ idempotent: true
1590
+ // pure read over POST — safe to replay on 5xx/429
1531
1591
  });
1532
1592
  return rows.map(normalizeMatch);
1533
1593
  }
@@ -2394,7 +2454,9 @@ var AvailabilityService = class {
2394
2454
  path: `/availability/${this.ctx.tenant}/availability/search`,
2395
2455
  auth: auth2,
2396
2456
  query: { site: siteCode, pageSize: productIds.length },
2397
- body: productIds
2457
+ body: productIds,
2458
+ idempotent: true
2459
+ // pure read over POST — safe to replay on 5xx/429
2398
2460
  });
2399
2461
  const byId = /* @__PURE__ */ new Map();
2400
2462
  for (const a of found) if (a.productId) byId.set(a.productId, a);
@@ -5112,7 +5174,7 @@ var ApprovalService = class {
5112
5174
  };
5113
5175
 
5114
5176
  // package.json
5115
- var version = "2.13.1";
5177
+ var version = "2.14.0";
5116
5178
 
5117
5179
  // src/version.ts
5118
5180
  var SDK_VERSION = version;
@@ -5341,8 +5403,10 @@ exports.EmporixClient = EmporixClient;
5341
5403
  exports.EmporixError = EmporixError;
5342
5404
  exports.EmporixForbiddenError = EmporixForbiddenError;
5343
5405
  exports.EmporixInsufficientScopeError = EmporixInsufficientScopeError;
5406
+ exports.EmporixNetworkError = EmporixNetworkError;
5344
5407
  exports.EmporixNotFoundError = EmporixNotFoundError;
5345
5408
  exports.EmporixServerError = EmporixServerError;
5409
+ exports.EmporixTimeoutError = EmporixTimeoutError;
5346
5410
  exports.EmporixValidationError = EmporixValidationError;
5347
5411
  exports.FeeService = FeeService;
5348
5412
  exports.HttpClient = HttpClient;