@misiki/gocommerce-connector 0.28.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.
- package/README.md +6 -0
- package/dist/{index-BaFtBtaA.d.cts → index-BdXhbm20.d.cts} +145 -24
- package/dist/{index-BaFtBtaA.d.ts → index-BdXhbm20.d.ts} +145 -24
- package/dist/index.cjs +204 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +204 -27
- package/dist/index.js.map +1 -1
- package/dist/services/index.cjs +202 -26
- package/dist/services/index.cjs.map +1 -1
- package/dist/services/index.d.cts +1 -1
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.js +202 -26
- package/dist/services/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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,
|
|
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';
|
package/dist/services/index.d.ts
CHANGED
|
@@ -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,
|
|
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';
|
package/dist/services/index.js
CHANGED
|
@@ -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 {
|
|
@@ -1572,7 +1595,23 @@ function attributesFrom(p) {
|
|
|
1572
1595
|
return Object.keys(out).length ? out : void 0;
|
|
1573
1596
|
}
|
|
1574
1597
|
var counts = (values) => Object.fromEntries((values ?? []).map((v) => [v.value, v.count]));
|
|
1575
|
-
function
|
|
1598
|
+
function categoryFacets(counted, tree) {
|
|
1599
|
+
const byId = new Map(tree.map((c) => [c.id, c]));
|
|
1600
|
+
const totals = /* @__PURE__ */ new Map();
|
|
1601
|
+
for (const { id: id2, count } of counted) {
|
|
1602
|
+
for (let c = byId.get(id2); c; c = c.parent_id === null ? void 0 : byId.get(c.parent_id)) {
|
|
1603
|
+
totals.set(c.id, (totals.get(c.id) ?? 0) + count);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
const kept = tree.filter((c) => totals.has(c.id)).sort((a, b) => a.position - b.position || a.id - b.id);
|
|
1607
|
+
const parentOf = (c) => c.parent_id !== null && byId.has(c.parent_id) ? c.parent_id : null;
|
|
1608
|
+
const under = (parent) => kept.filter((c) => parentOf(c) === parent).flatMap((c) => [{ name: c.title, slug: c.slug, count: totals.get(c.id), depth: c.depth }, ...under(c.id)]);
|
|
1609
|
+
return [
|
|
1610
|
+
...under(null),
|
|
1611
|
+
...counted.filter((c) => !byId.has(c.id)).map((c) => ({ name: c.title, slug: c.slug, count: c.count, depth: 0 }))
|
|
1612
|
+
];
|
|
1613
|
+
}
|
|
1614
|
+
function toStorefrontFacets(f, tree = []) {
|
|
1576
1615
|
if (!f) return { priceStat: {}, categories: [], tags: [], allFilters: {} };
|
|
1577
1616
|
const allFilters = {};
|
|
1578
1617
|
const group = (key, values) => {
|
|
@@ -1583,7 +1622,7 @@ function toStorefrontFacets(f) {
|
|
|
1583
1622
|
for (const [handle, values] of Object.entries(f.attributes ?? {})) group(`attributes.${handle}`, values);
|
|
1584
1623
|
return {
|
|
1585
1624
|
priceStat: f.price ? { min: toMajor(f.price.min), max: toMajor(f.price.max) } : {},
|
|
1586
|
-
categories: (f.categories ?? []
|
|
1625
|
+
categories: categoryFacets(f.categories ?? [], tree),
|
|
1587
1626
|
tags: (f.tags ?? []).map((t) => ({ name: t.value, count: t.count })),
|
|
1588
1627
|
allFilters
|
|
1589
1628
|
};
|
|
@@ -1616,13 +1655,14 @@ var SearchService = class extends CatalogService {
|
|
|
1616
1655
|
productTypes: listParam(p, "product_type"),
|
|
1617
1656
|
facets: true
|
|
1618
1657
|
});
|
|
1658
|
+
const tree = res.facets?.categories?.length ? await this.facts.categories().catch(() => []) : [];
|
|
1619
1659
|
return {
|
|
1620
1660
|
data: res.data,
|
|
1621
1661
|
count: res.count,
|
|
1622
1662
|
totalPages: res.noOfPage,
|
|
1623
1663
|
page: res.page,
|
|
1624
1664
|
categoryHierarchy: res.categoryHierarchy,
|
|
1625
|
-
facets: toStorefrontFacets(res.facets)
|
|
1665
|
+
facets: toStorefrontFacets(res.facets, tree)
|
|
1626
1666
|
};
|
|
1627
1667
|
} catch (err) {
|
|
1628
1668
|
if (err?.status === 404) return this.emptyResult();
|
|
@@ -2090,6 +2130,7 @@ var customizerService = new CustomizerService();
|
|
|
2090
2130
|
|
|
2091
2131
|
// src/services/checkout-service.ts
|
|
2092
2132
|
var CheckoutService = class extends BaseService {
|
|
2133
|
+
expected = /* @__PURE__ */ new Map();
|
|
2093
2134
|
/** Delivery options for the address on the cart: `{ data: [{ id, name, base_rate }] }`. */
|
|
2094
2135
|
async getShippingRates({ cartId } = {}) {
|
|
2095
2136
|
const id2 = cartId || storedCartId();
|
|
@@ -2261,6 +2302,64 @@ var CheckoutService = class extends BaseService {
|
|
|
2261
2302
|
);
|
|
2262
2303
|
return { methods: options.methods ?? [], currency: options.currency };
|
|
2263
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
|
+
}
|
|
2264
2363
|
async redirectGateway() {
|
|
2265
2364
|
const { methods } = await this.listMethods();
|
|
2266
2365
|
return methods.find((m) => REDIRECT_GATEWAYS.has(m.code))?.code;
|
|
@@ -2276,15 +2375,8 @@ var CheckoutService = class extends BaseService {
|
|
|
2276
2375
|
* the engine refuses a key replayed with a different body.
|
|
2277
2376
|
*/
|
|
2278
2377
|
async place(code, cartId, extra = {}) {
|
|
2279
|
-
const id2 =
|
|
2280
|
-
if (!id2) throw new GoCommerceError("Your bag is empty.", 400);
|
|
2281
|
-
const extras = cartExtras(id2);
|
|
2282
|
-
const ship = extras.shippingAddress;
|
|
2283
|
-
if (!ship) throw new GoCommerceError("Add a delivery address first.", 400);
|
|
2284
|
-
const cart = await this.data(`/api/carts/${encodeURIComponent(id2)}`);
|
|
2285
|
-
const email = cart.email || (ship.email ? String(ship.email) : "");
|
|
2378
|
+
const { id: id2, extras, email, address, currency } = await this.storedCheckout(cartId);
|
|
2286
2379
|
if (!email) throw new GoCommerceError("Add an email address so we can send your receipt.", 400);
|
|
2287
|
-
const address = toEngineAddress(ship);
|
|
2288
2380
|
const body = {
|
|
2289
2381
|
cart_id: id2,
|
|
2290
2382
|
email,
|
|
@@ -2307,10 +2399,23 @@ var CheckoutService = class extends BaseService {
|
|
|
2307
2399
|
...meta ? { meta_capi: meta } : {},
|
|
2308
2400
|
...attribution ? { attribution } : {}
|
|
2309
2401
|
};
|
|
2402
|
+
const expected = this.expected.get(id2);
|
|
2310
2403
|
const result = await this.data(`/api/checkout/${encodeURIComponent(code)}`, {
|
|
2311
2404
|
method: "POST",
|
|
2312
|
-
body:
|
|
2405
|
+
body: {
|
|
2406
|
+
...Object.keys(metadata).length ? { ...body, metadata } : body,
|
|
2407
|
+
...expected ? { quoted_total_minor: toMinor(expected.total, currency) } : {}
|
|
2408
|
+
},
|
|
2313
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);
|
|
2314
2419
|
});
|
|
2315
2420
|
if (result.order.access_token) {
|
|
2316
2421
|
rememberOrder({
|
|
@@ -2333,6 +2438,21 @@ var CheckoutService = class extends BaseService {
|
|
|
2333
2438
|
};
|
|
2334
2439
|
var REDIRECT_GATEWAYS = /* @__PURE__ */ new Set(["adyen", "paddle", "lemonsqueezy", "creem", "hyperswitch", "revenuecat"]);
|
|
2335
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
|
+
}
|
|
2336
2456
|
function newKey() {
|
|
2337
2457
|
const c = globalThis.crypto;
|
|
2338
2458
|
if (c?.randomUUID) return c.randomUUID();
|
|
@@ -2394,7 +2514,8 @@ var CouponService = class extends BaseService {
|
|
|
2394
2514
|
minAmount: o.minAmount,
|
|
2395
2515
|
description: o.applies ? o.title : o.remaining !== null ? `${o.title}. Add ${o.remaining} more to use this.` : o.reason || o.title,
|
|
2396
2516
|
applies: o.applies,
|
|
2397
|
-
remaining: o.remaining
|
|
2517
|
+
remaining: o.remaining,
|
|
2518
|
+
autoApply: o.autoApply
|
|
2398
2519
|
}));
|
|
2399
2520
|
return { data, count: data.length, pageSize: data.length, noOfPage: 1, page: 1 };
|
|
2400
2521
|
}
|
|
@@ -3150,15 +3271,18 @@ var StoreService = class extends BaseService {
|
|
|
3150
3271
|
async getStoreByIdOrDomain(_args = {}) {
|
|
3151
3272
|
const base = await readStaticStore();
|
|
3152
3273
|
const store = this.store;
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
if (isStoreMissing(err)) {
|
|
3274
|
+
const [plugins, live] = await Promise.all([
|
|
3275
|
+
this.facts.plugins().catch((err) => {
|
|
3276
|
+
if (store && isStoreMissing(err)) {
|
|
3156
3277
|
throw new GoCommerceError(`store not found: no store "${store}" on this engine`, 404, "store_not_found");
|
|
3157
3278
|
}
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
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" };
|
|
3162
3286
|
}
|
|
3163
3287
|
async getStore(storeId) {
|
|
3164
3288
|
return this.getStoreByIdOrDomain({ storeId });
|
|
@@ -3193,6 +3317,46 @@ var StoreService = class extends BaseService {
|
|
|
3193
3317
|
}
|
|
3194
3318
|
return super.post(path, data);
|
|
3195
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
|
+
}
|
|
3196
3360
|
async live() {
|
|
3197
3361
|
const [ready, plugins, menus, base, profile, ships] = await Promise.all([
|
|
3198
3362
|
this.facts.ready(),
|
|
@@ -3310,6 +3474,17 @@ var StoreService = class extends BaseService {
|
|
|
3310
3474
|
}
|
|
3311
3475
|
};
|
|
3312
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
|
+
}
|
|
3313
3488
|
var AD_TAGS = [
|
|
3314
3489
|
["meta-pixel", "pixel_id", "facebookPixel"],
|
|
3315
3490
|
["microsoft-clarity", "project_id", "microsoftClarity"],
|
|
@@ -3338,10 +3513,10 @@ function googleTags(plugins) {
|
|
|
3338
3513
|
}
|
|
3339
3514
|
function deliverable(base, ships) {
|
|
3340
3515
|
if (ships.everywhere || !ships.countries.length) return {};
|
|
3341
|
-
const
|
|
3342
|
-
const countries = ships.countries.map((code) =>
|
|
3343
|
-
const
|
|
3344
|
-
return { countries, ...
|
|
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] } };
|
|
3345
3520
|
}
|
|
3346
3521
|
function regionName(code) {
|
|
3347
3522
|
try {
|
|
@@ -3496,7 +3671,8 @@ function toSection(s) {
|
|
|
3496
3671
|
title: c.title,
|
|
3497
3672
|
slug: c.slug,
|
|
3498
3673
|
link: `/collections/${c.slug}`,
|
|
3499
|
-
image: mediaUrl(c.image) ?? null
|
|
3674
|
+
image: mediaUrl(c.image) ?? null,
|
|
3675
|
+
description: c.description || null
|
|
3500
3676
|
}));
|
|
3501
3677
|
}
|
|
3502
3678
|
return out;
|