@misiki/gocommerce-connector 0.29.0 → 0.30.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.
@@ -1 +1 @@
1
- export { b as AddressService, d as Approval, f as AuthService, g as AutocompleteService, B as B2bPlaced, h as B2bRole, i as B2bService, j as BannerService, k as BaseService, l as BlogService, o as CartService, p as CatalogService, r as CategoryService, s as ChatService, v as CheckoutService, y as CollectionService, z as Company, D as CompanyOrder, E as ContactService, F as CountryService, G as CouponService, I as Credit, J as CurrencyService, N as CustomizerEditor, O as CustomizerFile, Q as CustomizerSavedDesign, R as CustomizerService, U as DealService, V as DemoRequestService, W as EngineRequest, X as EnquiryService, $ as FaqService, a0 as FeedbackService, a1 as FooterSettings, a4 as GalleryService, a6 as GoogleReview, a7 as GoogleReviewService, a8 as GoogleReviews, aa as HeaderSettings, ab as HomeLayout, ac as HomeLayoutSection, ad as HomeService, af as InitService, ag as Invitation, ah as LayoutCollection, ai as LayoutPage, aj as LayoutService, ak as Lead, am as MeilisearchService, an as Member, aq as MenuService, at as NewsletterService, ay as OrderService, az as PageLayout, aA as PageService, aC as PaymentMethodService, aE as PluginService, aE as PluginsService, aF as PopularSearchService, aG as PopularityService, aL as ProductService, aM as ProfileService, aN as Quote, aO as REDIRECT_GATEWAYS, aP as ReelsService, aQ as RegionService, aR as RejectedLine, aU as ReviewService, aX as SearchService, aY as SettingService, aY as SettingsService, a_ as SiteChrome, a$ as SiteFooter, b0 as SiteHeader, b1 as SocialLink, b2 as StateService, b4 as StoreService, b6 as StorefrontMenu, b9 as ThemeSettings, ba as ThemeSettingsResult, bb as UploadService, bc as UserService, bf as VariantService, bg as VarniCustomDesignService, bh as VarniCustomProductService, bj as VendorService, bk as WarrantyService, bl as WhatsappConsent, bm as WhatsappConsentOffer, bn as WhatsappService, bq as WishlistService, br as addressService, bs as authService, bt as autocompleteService, bu as b2bService, bv as bannerService, bw as blogService, bx as cartService, by as categoryService, bz as chatService, bA as checkoutService, bB as collectionService, bC as contactService, bD as countryService, bE as couponService, bG as currencyService, bH as customizerService, bI as dealService, bJ as demoRequestService, bL as emptyPage, bM as enquiryService, bN as faqService, bO as feedbackService, bS as galleryService, bT as googleReviewService, bU as homeService, bV as initService, bW as layoutService, bX as mediaUrl, bY as meilisearchService, bZ as menuService, b$ as newsletterService, c0 as orderService, c1 as ownDomain, c2 as pageService, c3 as paymentMethodService, c4 as pluginService, c5 as pluginsService, c6 as popularSearchService, c7 as popularityService, ca as productService, cc as profileService, cd as qs, ce as reelsService, cf as regionService, cg as reviewService, ch as searchService, ci as setRequestHost, cj as settingService, ck as settingsService, cl as stateService, cm as staticStore, cn as storeForHost, co as storeService, cC as toPage, cF as toStorefrontFacets, cJ as uploadService, cK as userService, cL as variantService, cM as varniCustomDesignService, cN as varniCustomProductService, cO as vendorService, cP as warrantyService, cQ as whatsappService, cR as wishlistService } from '../index-DOlNI60Y.cjs';
1
+ export { b as AddressService, d as Approval, f as AuthService, g as AutocompleteService, B as B2bPlaced, h as B2bRole, i as B2bService, j as BannerService, k as BaseService, l as BlogService, o as CartService, p as CatalogService, r as CategoryService, s as ChatService, w as CheckoutService, z as CollectionService, D as Company, E as CompanyOrder, F as ContactService, G as CountryService, H as CouponService, J as Credit, K as CurrencyService, O as CustomizerEditor, P as CustomizerFile, R as CustomizerSavedDesign, S as CustomizerService, V as DealService, W as DemoRequestService, X as EngineRequest, Y as EnquiryService, a0 as FaqService, a1 as FeedbackService, a2 as FooterSettings, a5 as GalleryService, a7 as GoogleReview, a8 as GoogleReviewService, a9 as GoogleReviews, ab as HeaderSettings, ac as HomeLayout, ad as HomeLayoutSection, ae as HomeService, ag as InitService, ah as Invitation, ai as LayoutCollection, aj as LayoutPage, ak as LayoutService, al as Lead, an as MeilisearchService, ao as Member, ar as MenuService, au as NewsletterService, az as OrderService, aA as PageLayout, aB as PageService, aD as PaymentMethodService, aF as PluginService, aF as PluginsService, aG as PopularSearchService, aH as PopularityService, aM as ProductService, aN as ProfileService, aO as Quote, aP as REDIRECT_GATEWAYS, aQ as ReelsService, aR as RegionService, aS as RejectedLine, aV as ReviewService, aY as SearchService, aZ as SettingService, aZ as SettingsService, a$ as SiteChrome, b0 as SiteFooter, b1 as SiteHeader, b2 as SocialLink, b3 as StateService, b4 as StoreClosed, b5 as StorePass, b8 as StoreService, b9 as StoreUnlock, bb as StorefrontMenu, be as ThemeSettings, bf as ThemeSettingsResult, bg as UploadService, bh as UserService, bk as VariantService, bl as VarniCustomDesignService, bm as VarniCustomProductService, bo as VendorService, bp as WarrantyService, bq as WhatsappConsent, br as WhatsappConsentOffer, bs as WhatsappService, bv as WishlistService, bw as addressService, bx as authService, by as autocompleteService, bz as b2bService, bA as bannerService, bB as blogService, bC as cartService, bD as categoryService, bE as chatService, bF as checkoutService, bG as collectionService, bH as contactService, bI as countryService, bJ as couponService, bL as currencyService, bM as customizerService, bN as dealService, bO as demoRequestService, bQ as emptyPage, bR as enquiryService, bS as faqService, bT as feedbackService, bX as galleryService, bY as googleReviewService, bZ as homeService, b_ as initService, b$ as layoutService, c0 as mediaUrl, c1 as meilisearchService, c2 as menuService, c4 as newsletterService, c5 as orderService, c6 as ownDomain, c7 as pageService, c8 as paymentMethodService, c9 as pluginService, ca as pluginsService, cb as popularSearchService, cc as popularityService, cf as productService, ch as profileService, ci as qs, cj as reelsService, ck as regionService, cl as reviewService, cm as searchService, cn as setRequestHost, co as settingService, cp as settingsService, cq as stateService, cr as staticStore, cs as storeForHost, ct as storeService, cI as toPage, cL as toStorefrontFacets, cP as uploadService, cQ as userService, cR as variantService, cS as varniCustomDesignService, cT as varniCustomProductService, cU as vendorService, cV as warrantyService, cW as whatsappService, cX as wishlistService } from '../index-BdXhbm20.cjs';
@@ -1 +1 @@
1
- export { b as AddressService, d as Approval, f as AuthService, g as AutocompleteService, B as B2bPlaced, h as B2bRole, i as B2bService, j as BannerService, k as BaseService, l as BlogService, o as CartService, p as CatalogService, r as CategoryService, s as ChatService, v as CheckoutService, y as CollectionService, z as Company, D as CompanyOrder, E as ContactService, F as CountryService, G as CouponService, I as Credit, J as CurrencyService, N as CustomizerEditor, O as CustomizerFile, Q as CustomizerSavedDesign, R as CustomizerService, U as DealService, V as DemoRequestService, W as EngineRequest, X as EnquiryService, $ as FaqService, a0 as FeedbackService, a1 as FooterSettings, a4 as GalleryService, a6 as GoogleReview, a7 as GoogleReviewService, a8 as GoogleReviews, aa as HeaderSettings, ab as HomeLayout, ac as HomeLayoutSection, ad as HomeService, af as InitService, ag as Invitation, ah as LayoutCollection, ai as LayoutPage, aj as LayoutService, ak as Lead, am as MeilisearchService, an as Member, aq as MenuService, at as NewsletterService, ay as OrderService, az as PageLayout, aA as PageService, aC as PaymentMethodService, aE as PluginService, aE as PluginsService, aF as PopularSearchService, aG as PopularityService, aL as ProductService, aM as ProfileService, aN as Quote, aO as REDIRECT_GATEWAYS, aP as ReelsService, aQ as RegionService, aR as RejectedLine, aU as ReviewService, aX as SearchService, aY as SettingService, aY as SettingsService, a_ as SiteChrome, a$ as SiteFooter, b0 as SiteHeader, b1 as SocialLink, b2 as StateService, b4 as StoreService, b6 as StorefrontMenu, b9 as ThemeSettings, ba as ThemeSettingsResult, bb as UploadService, bc as UserService, bf as VariantService, bg as VarniCustomDesignService, bh as VarniCustomProductService, bj as VendorService, bk as WarrantyService, bl as WhatsappConsent, bm as WhatsappConsentOffer, bn as WhatsappService, bq as WishlistService, br as addressService, bs as authService, bt as autocompleteService, bu as b2bService, bv as bannerService, bw as blogService, bx as cartService, by as categoryService, bz as chatService, bA as checkoutService, bB as collectionService, bC as contactService, bD as countryService, bE as couponService, bG as currencyService, bH as customizerService, bI as dealService, bJ as demoRequestService, bL as emptyPage, bM as enquiryService, bN as faqService, bO as feedbackService, bS as galleryService, bT as googleReviewService, bU as homeService, bV as initService, bW as layoutService, bX as mediaUrl, bY as meilisearchService, bZ as menuService, b$ as newsletterService, c0 as orderService, c1 as ownDomain, c2 as pageService, c3 as paymentMethodService, c4 as pluginService, c5 as pluginsService, c6 as popularSearchService, c7 as popularityService, ca as productService, cc as profileService, cd as qs, ce as reelsService, cf as regionService, cg as reviewService, ch as searchService, ci as setRequestHost, cj as settingService, ck as settingsService, cl as stateService, cm as staticStore, cn as storeForHost, co as storeService, cC as toPage, cF as toStorefrontFacets, cJ as uploadService, cK as userService, cL as variantService, cM as varniCustomDesignService, cN as varniCustomProductService, cO as vendorService, cP as warrantyService, cQ as whatsappService, cR as wishlistService } from '../index-DOlNI60Y.js';
1
+ export { b as AddressService, d as Approval, f as AuthService, g as AutocompleteService, B as B2bPlaced, h as B2bRole, i as B2bService, j as BannerService, k as BaseService, l as BlogService, o as CartService, p as CatalogService, r as CategoryService, s as ChatService, w as CheckoutService, z as CollectionService, D as Company, E as CompanyOrder, F as ContactService, G as CountryService, H as CouponService, J as Credit, K as CurrencyService, O as CustomizerEditor, P as CustomizerFile, R as CustomizerSavedDesign, S as CustomizerService, V as DealService, W as DemoRequestService, X as EngineRequest, Y as EnquiryService, a0 as FaqService, a1 as FeedbackService, a2 as FooterSettings, a5 as GalleryService, a7 as GoogleReview, a8 as GoogleReviewService, a9 as GoogleReviews, ab as HeaderSettings, ac as HomeLayout, ad as HomeLayoutSection, ae as HomeService, ag as InitService, ah as Invitation, ai as LayoutCollection, aj as LayoutPage, ak as LayoutService, al as Lead, an as MeilisearchService, ao as Member, ar as MenuService, au as NewsletterService, az as OrderService, aA as PageLayout, aB as PageService, aD as PaymentMethodService, aF as PluginService, aF as PluginsService, aG as PopularSearchService, aH as PopularityService, aM as ProductService, aN as ProfileService, aO as Quote, aP as REDIRECT_GATEWAYS, aQ as ReelsService, aR as RegionService, aS as RejectedLine, aV as ReviewService, aY as SearchService, aZ as SettingService, aZ as SettingsService, a$ as SiteChrome, b0 as SiteFooter, b1 as SiteHeader, b2 as SocialLink, b3 as StateService, b4 as StoreClosed, b5 as StorePass, b8 as StoreService, b9 as StoreUnlock, bb as StorefrontMenu, be as ThemeSettings, bf as ThemeSettingsResult, bg as UploadService, bh as UserService, bk as VariantService, bl as VarniCustomDesignService, bm as VarniCustomProductService, bo as VendorService, bp as WarrantyService, bq as WhatsappConsent, br as WhatsappConsentOffer, bs as WhatsappService, bv as WishlistService, bw as addressService, bx as authService, by as autocompleteService, bz as b2bService, bA as bannerService, bB as blogService, bC as cartService, bD as categoryService, bE as chatService, bF as checkoutService, bG as collectionService, bH as contactService, bI as countryService, bJ as couponService, bL as currencyService, bM as customizerService, bN as dealService, bO as demoRequestService, bQ as emptyPage, bR as enquiryService, bS as faqService, bT as feedbackService, bX as galleryService, bY as googleReviewService, bZ as homeService, b_ as initService, b$ as layoutService, c0 as mediaUrl, c1 as meilisearchService, c2 as menuService, c4 as newsletterService, c5 as orderService, c6 as ownDomain, c7 as pageService, c8 as paymentMethodService, c9 as pluginService, ca as pluginsService, cb as popularSearchService, cc as popularityService, cf as productService, ch as profileService, ci as qs, cj as reelsService, ck as regionService, cl as reviewService, cm as searchService, cn as setRequestHost, co as settingService, cp as settingsService, cq as stateService, cr as staticStore, cs as storeForHost, ct as storeService, cI as toPage, cL as toStorefrontFacets, cP as uploadService, cQ as userService, cR as variantService, cS as varniCustomDesignService, cT as varniCustomProductService, cU as vendorService, cV as warrantyService, cW as whatsappService, cX as wishlistService } from '../index-BdXhbm20.js';
@@ -1085,7 +1085,22 @@ function toCartOffer(o, currency) {
1085
1085
  applies: o.applies,
1086
1086
  amount: o.amount ? toMajor(o.amount) : null,
1087
1087
  remaining: o.remaining ? toMajor(o.remaining) : null,
1088
- reason: o.reason ?? ""
1088
+ reason: o.reason ?? "",
1089
+ autoApply: !!o.auto_apply
1090
+ };
1091
+ }
1092
+ function toCheckoutQuote(q) {
1093
+ const d = q.discount;
1094
+ return {
1095
+ subtotal: toMajor(q.subtotal),
1096
+ shippingCharges: toMajor(q.shipping),
1097
+ shippingMethod: q.shipping_method ?? null,
1098
+ discount: toMajor(d?.amount),
1099
+ coupon: d ? { code: d.code, title: d.title, autoApplied: d.auto_applied, freeShipping: !!d.free_shipping } : null,
1100
+ tax: toMajor(q.tax),
1101
+ taxIncluded: q.tax_inclusive,
1102
+ codCharges: toMajor(q.payment_fee),
1103
+ total: toMajor(q.total)
1089
1104
  };
1090
1105
  }
1091
1106
  function toFreeShipping(f) {
@@ -1110,6 +1125,14 @@ function remember(key, load, ttl = TTL_MS) {
1110
1125
  });
1111
1126
  return value;
1112
1127
  }
1128
+ var known = /* @__PURE__ */ new Map();
1129
+ function keep(key, value) {
1130
+ known.set(key, value);
1131
+ return value;
1132
+ }
1133
+ function recall(key) {
1134
+ return known.get(key);
1135
+ }
1113
1136
 
1114
1137
  // src/services/facts.ts
1115
1138
  var StoreFacts = class extends BaseService {
@@ -2107,6 +2130,7 @@ var customizerService = new CustomizerService();
2107
2130
 
2108
2131
  // src/services/checkout-service.ts
2109
2132
  var CheckoutService = class extends BaseService {
2133
+ expected = /* @__PURE__ */ new Map();
2110
2134
  /** Delivery options for the address on the cart: `{ data: [{ id, name, base_rate }] }`. */
2111
2135
  async getShippingRates({ cartId } = {}) {
2112
2136
  const id2 = cartId || storedCartId();
@@ -2278,6 +2302,64 @@ var CheckoutService = class extends BaseService {
2278
2302
  );
2279
2303
  return { methods: options.methods ?? [], currency: options.currency };
2280
2304
  }
2305
+ /**
2306
+ * What checkout would charge for the cart now, by the storefront's method
2307
+ * code (`COD`, `RAZORPAY`, `PAYPAL`) or none yet (D185). Null until the
2308
+ * address step has stored an address, and null from an engine before D185.
2309
+ *
2310
+ * A refusal is checkout's own: the same GoCommerceError, with the same
2311
+ * message, that placing the order would raise now.
2312
+ */
2313
+ async getQuote({ cartId, method = "" } = {}) {
2314
+ const id2 = cartId || storedCartId();
2315
+ if (!id2 || !cartExtras(id2).shippingAddress) return null;
2316
+ const { extras, email, address } = await this.storedCheckout(id2);
2317
+ const code = method.toLowerCase() === "paypal" ? await this.redirectGateway() : method.toLowerCase();
2318
+ try {
2319
+ const quote2 = await this.data("/api/checkout/quote", {
2320
+ query: {
2321
+ cart: id2,
2322
+ country: address.country,
2323
+ state: address.state,
2324
+ shipping_rate_id: extras.shippingRateId || void 0,
2325
+ method: code || void 0,
2326
+ email: email || void 0
2327
+ }
2328
+ });
2329
+ return toCheckoutQuote(quote2);
2330
+ } catch (err) {
2331
+ if (err instanceof GoCommerceError && (err.status === 405 || err.status === 404 && err.message.startsWith("no route for "))) {
2332
+ return null;
2333
+ }
2334
+ throw err;
2335
+ }
2336
+ }
2337
+ /**
2338
+ * The total on screen as the shopper confirms, from getQuote, or null when nothing was quoted
2339
+ * (D185). Every order placed for the cart then carries it, and checkout refuses one whose own
2340
+ * total differs instead of charging it: `onChange` gets checkout's figures now and the sentence
2341
+ * the placing call fails with.
2342
+ */
2343
+ expectTotal({
2344
+ cartId,
2345
+ total,
2346
+ onChange
2347
+ }) {
2348
+ const id2 = cartId || storedCartId();
2349
+ if (!id2) return;
2350
+ if (typeof total === "number") this.expected.set(id2, { total, onChange });
2351
+ else this.expected.delete(id2);
2352
+ }
2353
+ async storedCheckout(cartId) {
2354
+ const id2 = cartId || storedCartId();
2355
+ if (!id2) throw new GoCommerceError("Your bag is empty.", 400);
2356
+ const extras = cartExtras(id2);
2357
+ const ship = extras.shippingAddress;
2358
+ if (!ship) throw new GoCommerceError("Add a delivery address first.", 400);
2359
+ const cart = await this.data(`/api/carts/${encodeURIComponent(id2)}`);
2360
+ const email = cart.email || (ship.email ? String(ship.email) : "");
2361
+ return { id: id2, extras, email, address: toEngineAddress(ship), currency: cart.currency };
2362
+ }
2281
2363
  async redirectGateway() {
2282
2364
  const { methods } = await this.listMethods();
2283
2365
  return methods.find((m) => REDIRECT_GATEWAYS.has(m.code))?.code;
@@ -2293,15 +2375,8 @@ var CheckoutService = class extends BaseService {
2293
2375
  * the engine refuses a key replayed with a different body.
2294
2376
  */
2295
2377
  async place(code, cartId, extra = {}) {
2296
- const id2 = cartId || storedCartId();
2297
- if (!id2) throw new GoCommerceError("Your bag is empty.", 400);
2298
- const extras = cartExtras(id2);
2299
- const ship = extras.shippingAddress;
2300
- if (!ship) throw new GoCommerceError("Add a delivery address first.", 400);
2301
- const cart = await this.data(`/api/carts/${encodeURIComponent(id2)}`);
2302
- const email = cart.email || (ship.email ? String(ship.email) : "");
2378
+ const { id: id2, extras, email, address, currency } = await this.storedCheckout(cartId);
2303
2379
  if (!email) throw new GoCommerceError("Add an email address so we can send your receipt.", 400);
2304
- const address = toEngineAddress(ship);
2305
2380
  const body = {
2306
2381
  cart_id: id2,
2307
2382
  email,
@@ -2324,10 +2399,23 @@ var CheckoutService = class extends BaseService {
2324
2399
  ...meta ? { meta_capi: meta } : {},
2325
2400
  ...attribution ? { attribution } : {}
2326
2401
  };
2402
+ const expected = this.expected.get(id2);
2327
2403
  const result = await this.data(`/api/checkout/${encodeURIComponent(code)}`, {
2328
2404
  method: "POST",
2329
- body: Object.keys(metadata).length ? { ...body, metadata } : body,
2405
+ body: {
2406
+ ...Object.keys(metadata).length ? { ...body, metadata } : body,
2407
+ ...expected ? { quoted_total_minor: toMinor(expected.total, currency) } : {}
2408
+ },
2330
2409
  headers: { "Idempotency-Key": key }
2410
+ }).catch((err) => {
2411
+ if (keyAlreadyUsed(err)) {
2412
+ throw new GoCommerceError("This order was already placed in another tab. Check that tab for its confirmation.", 400, err.code);
2413
+ }
2414
+ const now = movedTotal(err);
2415
+ if (!expected || !now) throw err;
2416
+ const message = `Your total changed to ${shopperAmount(now.total, currency)}. Check the new total and confirm again.`;
2417
+ expected.onChange?.(now, message);
2418
+ throw new GoCommerceError(message, 409, "total_changed", now);
2331
2419
  });
2332
2420
  if (result.order.access_token) {
2333
2421
  rememberOrder({
@@ -2350,6 +2438,21 @@ var CheckoutService = class extends BaseService {
2350
2438
  };
2351
2439
  var REDIRECT_GATEWAYS = /* @__PURE__ */ new Set(["adyen", "paddle", "lemonsqueezy", "creem", "hyperswitch", "revenuecat"]);
2352
2440
  var unsupported = (method, why) => new NotSupportedError("CheckoutService", method, why);
2441
+ function keyAlreadyUsed(err) {
2442
+ return err instanceof GoCommerceError && err.status === 400 && err.message.includes("Idempotency-Key was already used");
2443
+ }
2444
+ function movedTotal(err) {
2445
+ if (!(err instanceof GoCommerceError) || err.code !== "total_changed") return null;
2446
+ const now = err.details;
2447
+ return now?.total ? toCheckoutQuote(now) : null;
2448
+ }
2449
+ function shopperAmount(amount, currency) {
2450
+ return new Intl.NumberFormat(currency === "INR" ? "en-IN" : "en", {
2451
+ style: "currency",
2452
+ currency,
2453
+ ...Number.isInteger(amount) ? { minimumFractionDigits: 0 } : {}
2454
+ }).format(amount);
2455
+ }
2353
2456
  function newKey() {
2354
2457
  const c = globalThis.crypto;
2355
2458
  if (c?.randomUUID) return c.randomUUID();
@@ -2411,7 +2514,8 @@ var CouponService = class extends BaseService {
2411
2514
  minAmount: o.minAmount,
2412
2515
  description: o.applies ? o.title : o.remaining !== null ? `${o.title}. Add ${o.remaining} more to use this.` : o.reason || o.title,
2413
2516
  applies: o.applies,
2414
- remaining: o.remaining
2517
+ remaining: o.remaining,
2518
+ autoApply: o.autoApply
2415
2519
  }));
2416
2520
  return { data, count: data.length, pageSize: data.length, noOfPage: 1, page: 1 };
2417
2521
  }
@@ -3167,15 +3271,18 @@ var StoreService = class extends BaseService {
3167
3271
  async getStoreByIdOrDomain(_args = {}) {
3168
3272
  const base = await readStaticStore();
3169
3273
  const store = this.store;
3170
- if (store) {
3171
- await this.facts.plugins().catch((err) => {
3172
- if (isStoreMissing(err)) {
3274
+ const [plugins, live] = await Promise.all([
3275
+ this.facts.plugins().catch((err) => {
3276
+ if (store && isStoreMissing(err)) {
3173
3277
  throw new GoCommerceError(`store not found: no store "${store}" on this engine`, 404, "store_not_found");
3174
3278
  }
3175
- });
3176
- }
3177
- const live = await this.live().catch(() => ({}));
3178
- return { ...base, ...live, id: base.id || "gocommerce" };
3279
+ return void 0;
3280
+ }),
3281
+ this.live().catch(() => ({}))
3282
+ ]);
3283
+ const known2 = `${this.creds.apiUrl ?? ""}|${store ?? ""}|closed`;
3284
+ const closed = plugins ? keep(known2, closedOf(plugins)) : recall(known2);
3285
+ return { ...base, ...live, closed, id: base.id || "gocommerce" };
3179
3286
  }
3180
3287
  async getStore(storeId) {
3181
3288
  return this.getStoreByIdOrDomain({ storeId });
@@ -3210,6 +3317,46 @@ var StoreService = class extends BaseService {
3210
3317
  }
3211
3318
  return super.post(path, data);
3212
3319
  }
3320
+ /**
3321
+ * A closed store's password (D170), traded for a pass. Never throws. The
3322
+ * engine allows 20 tries an hour per client, and sees this server as the
3323
+ * client unless `clientAddress`, the shopper's, travels as `X-Forwarded-For`.
3324
+ */
3325
+ async unlockStore(password, { clientAddress } = {}) {
3326
+ const typed = password.trim();
3327
+ if (!typed) return { outcome: "wrong" };
3328
+ try {
3329
+ return { outcome: "unlocked", pass: await this.storePass({ password: typed }, clientAddress) };
3330
+ } catch (err) {
3331
+ return { outcome: err instanceof GoCommerceError && UNLOCK_REFUSED[err.status] || "unavailable" };
3332
+ }
3333
+ }
3334
+ /**
3335
+ * A pass checked without spending a try: the pass, or null when it is wrong,
3336
+ * expired, ended by a new password, or the store takes none now. Throws when
3337
+ * the engine cannot say.
3338
+ */
3339
+ async checkStorePass(token) {
3340
+ if (!token) return null;
3341
+ try {
3342
+ return await this.storePass({ token });
3343
+ } catch (err) {
3344
+ if (err instanceof GoCommerceError && (err.status === 422 || err.status === 404)) return null;
3345
+ throw err;
3346
+ }
3347
+ }
3348
+ async storePass(body, clientAddress) {
3349
+ const answer = await this.data("/api/store/password", {
3350
+ method: "POST",
3351
+ body,
3352
+ headers: clientAddress && ONE_IP_ADDRESS.test(clientAddress) ? { "X-Forwarded-For": clientAddress } : void 0
3353
+ });
3354
+ const expiresAt = new Date(answer?.expires_at ?? NaN);
3355
+ if (!answer?.token || Number.isNaN(expiresAt.getTime())) {
3356
+ throw new GoCommerceError("The commerce engine answered a store pass without its token or expiry.", 502);
3357
+ }
3358
+ return { token: answer.token, expiresAt };
3359
+ }
3213
3360
  async live() {
3214
3361
  const [ready, plugins, menus, base, profile, ships] = await Promise.all([
3215
3362
  this.facts.ready(),
@@ -3327,6 +3474,17 @@ var StoreService = class extends BaseService {
3327
3474
  }
3328
3475
  };
3329
3476
  var storeService = new StoreService();
3477
+ var ONE_IP_ADDRESS = /^[\da-f.:]{2,45}$/i;
3478
+ var UNLOCK_REFUSED = {
3479
+ 422: "wrong",
3480
+ 429: "throttled",
3481
+ 404: "open"
3482
+ };
3483
+ function closedOf(plugins) {
3484
+ const plugin = plugins?.get("store-close");
3485
+ if (!plugin) return void 0;
3486
+ return { message: String(plugin.settings?.message ?? ""), password: plugin.settings?.password === true };
3487
+ }
3330
3488
  var AD_TAGS = [
3331
3489
  ["meta-pixel", "pixel_id", "facebookPixel"],
3332
3490
  ["microsoft-clarity", "project_id", "microsoftClarity"],
@@ -3355,10 +3513,10 @@ function googleTags(plugins) {
3355
3513
  }
3356
3514
  function deliverable(base, ships) {
3357
3515
  if (ships.everywhere || !ships.countries.length) return {};
3358
- const known = new Map((base.countries ?? []).map((c) => [String(c.code).toUpperCase(), c]));
3359
- const countries = ships.countries.map((code) => known.get(code) ?? { code, name: regionName(code) });
3360
- const keep = base.country && ships.countries.includes(String(base.country.code).toUpperCase());
3361
- return { countries, ...keep ? {} : { country: countries[0] } };
3516
+ const known2 = new Map((base.countries ?? []).map((c) => [String(c.code).toUpperCase(), c]));
3517
+ const countries = ships.countries.map((code) => known2.get(code) ?? { code, name: regionName(code) });
3518
+ const keep2 = base.country && ships.countries.includes(String(base.country.code).toUpperCase());
3519
+ return { countries, ...keep2 ? {} : { country: countries[0] } };
3362
3520
  }
3363
3521
  function regionName(code) {
3364
3522
  try {
@@ -3513,7 +3671,8 @@ function toSection(s) {
3513
3671
  title: c.title,
3514
3672
  slug: c.slug,
3515
3673
  link: `/collections/${c.slug}`,
3516
- image: mediaUrl(c.image) ?? null
3674
+ image: mediaUrl(c.image) ?? null,
3675
+ description: c.description || null
3517
3676
  }));
3518
3677
  }
3519
3678
  return out;