@moonbase.sh/storefront-api 0.4.25 → 0.4.27

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.cjs CHANGED
@@ -28,33 +28,37 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
30
  // src/index.ts
31
- var src_exports = {};
32
- __export(src_exports, {
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
33
  ActivationMethod: () => ActivationMethod,
34
34
  ActivationRequestFulfillmentType: () => ActivationRequestFulfillmentType,
35
35
  ActivationRequestStatus: () => ActivationRequestStatus,
36
36
  ActivationStatus: () => ActivationStatus,
37
37
  ConsoleLogger: () => ConsoleLogger,
38
38
  CycleLength: () => CycleLength,
39
+ DiscountUtils: () => DiscountUtils,
39
40
  InMemoryStore: () => InMemoryStore,
40
41
  LicenseStatus: () => LicenseStatus,
41
42
  LocalStorageStore: () => LocalStorageStore,
42
43
  LogLevel: () => LogLevel,
44
+ MoneyCollectionUtils: () => MoneyCollectionUtils,
43
45
  MoonbaseApi: () => MoonbaseApi,
44
46
  MoonbaseClient: () => MoonbaseClient,
45
47
  MoonbaseError: () => MoonbaseError,
46
48
  NotAuthenticatedError: () => NotAuthenticatedError,
47
49
  NotAuthorizedError: () => NotAuthorizedError,
48
50
  NotFoundError: () => NotFoundError,
51
+ OfferUtils: () => OfferUtils,
49
52
  OrderStatus: () => OrderStatus,
50
53
  Platform: () => Platform,
51
54
  SubscriptionStatus: () => SubscriptionStatus,
52
55
  TokenStore: () => TokenStore,
56
+ objectToQuery: () => objectToQuery,
53
57
  problemDetailsSchema: () => problemDetailsSchema,
54
58
  schemas: () => schemas_exports3,
55
59
  utmToObject: () => utmToObject
56
60
  });
57
- module.exports = __toCommonJS(src_exports);
61
+ module.exports = __toCommonJS(index_exports);
58
62
 
59
63
  // src/activationRequests/endpoints.ts
60
64
  var import_zod5 = require("zod");
@@ -216,7 +220,7 @@ var storefrontProductSchema = import_zod3.z.object({
216
220
  downloads: downloadSchema.array().optional(),
217
221
  defaultVariation: pricingVariationSchema.optional(),
218
222
  variations: pricingVariationSchema.array().optional(),
219
- type: import_zod3.z.void().transform(() => "product").pipe(import_zod3.z.literal("product"))
223
+ type: import_zod3.z.string().optional().default("Product").pipe(import_zod3.z.literal("Product"))
220
224
  });
221
225
  var storefrontBundleSchema = import_zod3.z.object({
222
226
  id: import_zod3.z.string(),
@@ -231,12 +235,28 @@ var storefrontBundleSchema = import_zod3.z.object({
231
235
  })).array(),
232
236
  defaultVariation: pricingVariationSchema.optional(),
233
237
  variations: pricingVariationSchema.array().optional(),
234
- type: import_zod3.z.void().transform(() => "bundle").pipe(import_zod3.z.literal("bundle"))
238
+ type: import_zod3.z.string().optional().default("Bundle").pipe(import_zod3.z.literal("Bundle"))
239
+ });
240
+ var cartContainsItemsConditionSchema = import_zod3.z.object({
241
+ type: import_zod3.z.literal("CartContainsItems"),
242
+ minimumItems: import_zod3.z.number(),
243
+ relevantItemVariations: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.string().array())
244
+ });
245
+ var offerConditionSchema = import_zod3.z.discriminatedUnion("type", [cartContainsItemsConditionSchema]);
246
+ var storefrontOfferSchema = import_zod3.z.object({
247
+ id: import_zod3.z.string(),
248
+ target: import_zod3.z.union([storefrontProductSchema, storefrontBundleSchema]),
249
+ targetVariations: import_zod3.z.string().array(),
250
+ condition: offerConditionSchema,
251
+ discount: discountSchema
235
252
  });
236
253
  var storefrontSchema = import_zod3.z.object({
237
254
  suggestedCurrency: import_zod3.z.string(),
255
+ enabledCurrencies: import_zod3.z.string().array(),
238
256
  products: storefrontProductSchema.array(),
239
- bundles: storefrontBundleSchema.array()
257
+ bundles: storefrontBundleSchema.array(),
258
+ // Offers need to be optional since we may still have old, cached representations in browsers
259
+ offers: storefrontOfferSchema.array().optional()
240
260
  });
241
261
 
242
262
  // src/activationRequests/models.ts
@@ -660,6 +680,132 @@ var ProductEndpoints = class {
660
680
  }
661
681
  };
662
682
 
683
+ // src/utils/api.ts
684
+ var import_cross_fetch2 = __toESM(require("cross-fetch"), 1);
685
+ function objectToQuery(obj) {
686
+ return Object.entries(obj != null ? obj : {}).filter(([_, value]) => value !== void 0).map(([key, value]) => `${key}=${encodeURIComponent(value instanceof Date ? value.toISOString() : value)}`).join("&");
687
+ }
688
+ var MoonbaseApi = class {
689
+ constructor(baseUrl, tokenStore, logger) {
690
+ this.baseUrl = baseUrl;
691
+ this.tokenStore = tokenStore;
692
+ this.logger = logger;
693
+ }
694
+ async authenticatedFetch(path, schema, options) {
695
+ if (!this.tokenStore.user)
696
+ throw new NotAuthenticatedError();
697
+ return await this.fetch(path, schema, options);
698
+ }
699
+ async fetch(path, schema, options) {
700
+ var _a;
701
+ const accessToken = await this.tokenStore.getAccessToken();
702
+ const contentType = (_a = options == null ? void 0 : options.contentType) != null ? _a : "application/json";
703
+ this.logger.debug("Making request to Moonbase API...", {
704
+ path,
705
+ body: options == null ? void 0 : options.body
706
+ });
707
+ const startedAt = /* @__PURE__ */ new Date();
708
+ const request = {
709
+ method: (options == null ? void 0 : options.method) || "GET",
710
+ mode: "cors",
711
+ headers: {
712
+ "Accept": "application/json",
713
+ "Content-Type": contentType,
714
+ // While this fetch can be anonymous, we add the token if we have it
715
+ ...accessToken ? { Authorization: `Bearer ${accessToken}` } : {},
716
+ // Force CORS on all calls
717
+ "x-mb-cors": "1"
718
+ },
719
+ body: (options == null ? void 0 : options.body) ? contentType !== "application/json" ? options.body : JSON.stringify(options.body) : void 0,
720
+ signal: options == null ? void 0 : options.abort,
721
+ redirect: "manual"
722
+ };
723
+ const response = await (0, import_cross_fetch2.default)(this.baseUrl + path, request);
724
+ const finishedAt = /* @__PURE__ */ new Date();
725
+ this.logger.debug("Received response from Moonbase", {
726
+ path,
727
+ status: response.status,
728
+ duration: finishedAt.getTime() - startedAt.getTime()
729
+ });
730
+ if (response.status >= 400) {
731
+ try {
732
+ await handleResponseProblem(response, this.logger);
733
+ } catch (err) {
734
+ this.reportRequestProblem(path, request, response, err);
735
+ throw err;
736
+ }
737
+ }
738
+ let json;
739
+ try {
740
+ json = schema ? await response.json() : null;
741
+ return {
742
+ data: schema ? schema.parse(json) : null,
743
+ headers: Object.fromEntries(response.headers.entries()),
744
+ status: response.status
745
+ };
746
+ } catch (err) {
747
+ this.logger.warn("Could not parse response", {
748
+ status: response.status,
749
+ path,
750
+ content: json || (response.bodyUsed ? "unknown" : await response.text()),
751
+ headers: Object.fromEntries(response.headers.entries()),
752
+ userAgent: window && window.navigator && window.navigator.userAgent,
753
+ err
754
+ });
755
+ this.reportParsingProblem(path, err, json || (response.bodyUsed ? "unknown" : await response.text()));
756
+ throw new MoonbaseError("Bad response", "Could not parse server response", response.status);
757
+ }
758
+ }
759
+ async reportParsingProblem(path, err, body) {
760
+ try {
761
+ await (0, import_cross_fetch2.default)(`${this.baseUrl}/api/customer/insights/error`, {
762
+ mode: "cors",
763
+ method: "POST",
764
+ headers: {
765
+ "Accept": "application/json",
766
+ "Content-Type": "application/json"
767
+ },
768
+ body: JSON.stringify({
769
+ title: "Parse error",
770
+ detail: `Could not parse response body`,
771
+ path,
772
+ origin: window == null ? void 0 : window.location.href,
773
+ userAgent: window && window.navigator && window.navigator.userAgent,
774
+ err,
775
+ body
776
+ })
777
+ });
778
+ } catch (e) {
779
+ }
780
+ }
781
+ async reportRequestProblem(path, request, response, err) {
782
+ try {
783
+ await (0, import_cross_fetch2.default)(`${this.baseUrl}/api/customer/insights/warn`, {
784
+ mode: "cors",
785
+ method: "POST",
786
+ headers: {
787
+ "Accept": "application/json",
788
+ "Content-Type": "application/json"
789
+ },
790
+ body: JSON.stringify({
791
+ title: "Request error",
792
+ detail: `Request failed with status ${response.status}`,
793
+ status: response.status,
794
+ request: {
795
+ ...request,
796
+ headers: void 0,
797
+ body: void 0
798
+ },
799
+ error: err,
800
+ path,
801
+ origin: window == null ? void 0 : window.location.href
802
+ })
803
+ });
804
+ } catch (e) {
805
+ }
806
+ }
807
+ };
808
+
663
809
  // src/inventory/subscriptions/endpoints.ts
664
810
  var import_zod13 = require("zod");
665
811
 
@@ -721,6 +867,7 @@ var openProductLineItem = import_zod11.z.object({
721
867
  productId: import_zod11.z.string(),
722
868
  quantity: import_zod11.z.number(),
723
869
  variationId: import_zod11.z.string(),
870
+ offerId: import_zod11.z.string().optional(),
724
871
  isDefaultVariation: import_zod11.z.boolean().nullish(),
725
872
  isSubcriptionPayment: import_zod11.z.boolean().nullish(),
726
873
  price: priceCollectionSchema.optional(),
@@ -744,7 +891,9 @@ var openBundleLineItem = import_zod11.z.object({
744
891
  bundleId: import_zod11.z.string(),
745
892
  quantity: import_zod11.z.number(),
746
893
  variationId: import_zod11.z.string(),
894
+ offerId: import_zod11.z.string().optional(),
747
895
  partial: import_zod11.z.boolean().optional(),
896
+ replaced: import_zod11.z.string().array().optional(),
748
897
  isDefaultVariation: import_zod11.z.boolean().nullish(),
749
898
  isSubcriptionPayment: import_zod11.z.boolean().nullish(),
750
899
  price: priceCollectionSchema.optional(),
@@ -817,6 +966,8 @@ var subscriptionSchema = import_zod12.z.object({
817
966
  startedAt: import_zod12.z.coerce.date(),
818
967
  total: orderTotalSchema,
819
968
  cycleLength: import_zod12.z.nativeEnum(CycleLength),
969
+ paymentMethod: import_zod12.z.string().optional(),
970
+ embeddedUpdatePaymentUrl: import_zod12.z.string().optional(),
820
971
  content: import_zod12.z.discriminatedUnion("type", [
821
972
  import_zod12.z.object({
822
973
  type: import_zod12.z.literal("Product"),
@@ -841,8 +992,8 @@ var SubscriptionEndpoints = class {
841
992
  const response = await this.api.authenticatedFetch(nextUrl || "/api/customer/inventory/subscriptions", paged(subscriptionSchema));
842
993
  return response.data;
843
994
  }
844
- async getById(subscriptionId) {
845
- const response = await this.api.authenticatedFetch(`/api/customer/inventory/subscriptions/${subscriptionId}`, subscriptionSchema);
995
+ async getById(subscriptionId, options) {
996
+ const response = await this.api.authenticatedFetch(`/api/customer/inventory/subscriptions/${subscriptionId}?${objectToQuery(options)}`, subscriptionSchema);
846
997
  return response.data;
847
998
  }
848
999
  async cancel(subscriptionId) {
@@ -933,129 +1084,6 @@ var StorefrontEndpoints = class {
933
1084
  }
934
1085
  };
935
1086
 
936
- // src/utils/api.ts
937
- var import_cross_fetch2 = __toESM(require("cross-fetch"), 1);
938
- var MoonbaseApi = class {
939
- constructor(baseUrl, tokenStore, logger) {
940
- this.baseUrl = baseUrl;
941
- this.tokenStore = tokenStore;
942
- this.logger = logger;
943
- }
944
- async authenticatedFetch(path, schema, options) {
945
- if (!this.tokenStore.user)
946
- throw new NotAuthenticatedError();
947
- return await this.fetch(path, schema, options);
948
- }
949
- async fetch(path, schema, options) {
950
- var _a;
951
- const accessToken = await this.tokenStore.getAccessToken();
952
- const contentType = (_a = options == null ? void 0 : options.contentType) != null ? _a : "application/json";
953
- this.logger.debug("Making request to Moonbase API...", {
954
- path,
955
- body: options == null ? void 0 : options.body
956
- });
957
- const startedAt = /* @__PURE__ */ new Date();
958
- const request = {
959
- method: (options == null ? void 0 : options.method) || "GET",
960
- mode: "cors",
961
- headers: {
962
- "Accept": "application/json",
963
- "Content-Type": contentType,
964
- // While this fetch can be anonymous, we add the token if we have it
965
- ...accessToken ? { Authorization: `Bearer ${accessToken}` } : {},
966
- // Force CORS on all calls
967
- "x-mb-cors": "1"
968
- },
969
- body: (options == null ? void 0 : options.body) ? contentType !== "application/json" ? options.body : JSON.stringify(options.body) : void 0,
970
- signal: options == null ? void 0 : options.abort,
971
- redirect: "manual"
972
- };
973
- const response = await (0, import_cross_fetch2.default)(this.baseUrl + path, request);
974
- const finishedAt = /* @__PURE__ */ new Date();
975
- this.logger.debug("Received response from Moonbase", {
976
- path,
977
- status: response.status,
978
- duration: finishedAt.getTime() - startedAt.getTime()
979
- });
980
- if (response.status >= 400) {
981
- try {
982
- await handleResponseProblem(response, this.logger);
983
- } catch (err) {
984
- this.reportRequestProblem(path, request, response, err);
985
- throw err;
986
- }
987
- }
988
- let json;
989
- try {
990
- json = schema ? await response.json() : null;
991
- return {
992
- data: schema ? schema.parse(json) : null,
993
- headers: Object.fromEntries(response.headers.entries()),
994
- status: response.status
995
- };
996
- } catch (err) {
997
- this.logger.warn("Could not parse response", {
998
- status: response.status,
999
- path,
1000
- content: json || (response.bodyUsed ? "unknown" : await response.text()),
1001
- headers: Object.fromEntries(response.headers.entries()),
1002
- userAgent: window && window.navigator && window.navigator.userAgent,
1003
- err
1004
- });
1005
- this.reportParsingProblem(path, err, json || (response.bodyUsed ? "unknown" : await response.text()));
1006
- throw new MoonbaseError("Bad response", "Could not parse server response", response.status);
1007
- }
1008
- }
1009
- async reportParsingProblem(path, err, body) {
1010
- try {
1011
- await (0, import_cross_fetch2.default)(`${this.baseUrl}/api/customer/insights/error`, {
1012
- mode: "cors",
1013
- method: "POST",
1014
- headers: {
1015
- "Accept": "application/json",
1016
- "Content-Type": "application/json"
1017
- },
1018
- body: JSON.stringify({
1019
- title: "Parse error",
1020
- detail: `Could not parse response body`,
1021
- path,
1022
- origin: window == null ? void 0 : window.location.href,
1023
- userAgent: window && window.navigator && window.navigator.userAgent,
1024
- err,
1025
- body
1026
- })
1027
- });
1028
- } catch (e) {
1029
- }
1030
- }
1031
- async reportRequestProblem(path, request, response, err) {
1032
- try {
1033
- await (0, import_cross_fetch2.default)(`${this.baseUrl}/api/customer/insights/warn`, {
1034
- mode: "cors",
1035
- method: "POST",
1036
- headers: {
1037
- "Accept": "application/json",
1038
- "Content-Type": "application/json"
1039
- },
1040
- body: JSON.stringify({
1041
- title: "Request error",
1042
- detail: `Request failed with status ${response.status}`,
1043
- status: response.status,
1044
- request: {
1045
- ...request,
1046
- headers: void 0,
1047
- body: void 0
1048
- },
1049
- error: err,
1050
- path,
1051
- origin: window == null ? void 0 : window.location.href
1052
- })
1053
- });
1054
- } catch (e) {
1055
- }
1056
- }
1057
- };
1058
-
1059
1087
  // src/utils/tokenStore.ts
1060
1088
  var import_cross_fetch3 = __toESM(require("cross-fetch"), 1);
1061
1089
 
@@ -1304,6 +1332,55 @@ __export(schemas_exports3, {
1304
1332
  orders: () => schemas_exports2
1305
1333
  });
1306
1334
 
1335
+ // src/utils/discount.ts
1336
+ var DiscountUtils = class {
1337
+ static apply(discount, price) {
1338
+ switch (discount.type) {
1339
+ case "PercentageOffDiscount":
1340
+ return Object.fromEntries(Object.entries(price).map(([currency, amount]) => [currency, amount * discount.percentage]));
1341
+ case "FlatAmountOffDiscount":
1342
+ return Object.fromEntries(Object.entries(price).map(([currency, amount]) => {
1343
+ var _a, _b;
1344
+ return [currency, Math.min(amount, (_b = (_a = discount.total) == null ? void 0 : _a[currency]) != null ? _b : 0)];
1345
+ }));
1346
+ }
1347
+ }
1348
+ };
1349
+
1350
+ // src/utils/money.ts
1351
+ var MoneyCollectionUtils = class {
1352
+ static sum(a, b) {
1353
+ return Object.fromEntries(Object.entries(a).map(([currency, amount]) => [currency, amount + b[currency]]));
1354
+ }
1355
+ static subtract(a, b) {
1356
+ return Object.fromEntries(Object.entries(a).map(([currency, amount]) => [currency, amount - b[currency]]));
1357
+ }
1358
+ };
1359
+
1360
+ // src/utils/offer.ts
1361
+ var OfferUtils = class {
1362
+ static eligible(offer, order) {
1363
+ switch (offer.condition.type) {
1364
+ case "CartContainsItems":
1365
+ const relevantItems = order.items.filter(
1366
+ (i) => i.type === "Product" && offer.condition.relevantItemVariations[`Product/${i.productId}`] && (offer.condition.relevantItemVariations[`Product/${i.productId}`].length === 0 || offer.condition.relevantItemVariations[`Product/${i.productId}`].includes(i.variationId)) || i.type === "Bundle" && offer.condition.relevantItemVariations[`Bundle/${i.bundleId}`] && (offer.condition.relevantItemVariations[`Bundle/${i.bundleId}`].length === 0 || offer.condition.relevantItemVariations[`Bundle/${i.bundleId}`].includes(i.variationId))
1367
+ );
1368
+ console.log("Relevant items for offer:", { offer, relevantItems });
1369
+ return relevantItems.length >= offer.condition.minimumItems;
1370
+ }
1371
+ console.warn("Unsupported offer condition found:", offer.condition);
1372
+ return false;
1373
+ }
1374
+ static applyToVariation(offer, variation) {
1375
+ const discount = DiscountUtils.apply(offer.discount, variation.price);
1376
+ return {
1377
+ ...variation,
1378
+ price: MoneyCollectionUtils.subtract(variation.price, discount),
1379
+ hasDiscount: Object.values(discount).some((v) => v > 0) || variation.hasDiscount
1380
+ };
1381
+ }
1382
+ };
1383
+
1307
1384
  // src/index.ts
1308
1385
  var MoonbaseClient = class {
1309
1386
  constructor(configuration) {
@@ -1330,20 +1407,24 @@ var MoonbaseClient = class {
1330
1407
  ActivationStatus,
1331
1408
  ConsoleLogger,
1332
1409
  CycleLength,
1410
+ DiscountUtils,
1333
1411
  InMemoryStore,
1334
1412
  LicenseStatus,
1335
1413
  LocalStorageStore,
1336
1414
  LogLevel,
1415
+ MoneyCollectionUtils,
1337
1416
  MoonbaseApi,
1338
1417
  MoonbaseClient,
1339
1418
  MoonbaseError,
1340
1419
  NotAuthenticatedError,
1341
1420
  NotAuthorizedError,
1342
1421
  NotFoundError,
1422
+ OfferUtils,
1343
1423
  OrderStatus,
1344
1424
  Platform,
1345
1425
  SubscriptionStatus,
1346
1426
  TokenStore,
1427
+ objectToQuery,
1347
1428
  problemDetailsSchema,
1348
1429
  schemas,
1349
1430
  utmToObject