@misiki/gocommerce-connector 0.13.0 → 0.15.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.
@@ -1197,14 +1197,17 @@ var CatalogService = class extends BaseService {
1197
1197
  const attr = Object.entries(q.attributes ?? {}).flatMap(
1198
1198
  ([handle, values]) => values.filter(Boolean).map((v) => `${handle}:${v}`)
1199
1199
  );
1200
- if (q.facets && !legacyListing.has(this.engineBase())) {
1200
+ const refined = attr.length > 0 || !!(q.tags?.length || q.vendors?.length || q.productTypes?.length);
1201
+ const triedIndex = !!search && !refined;
1202
+ const counted = q.facets && !legacyListing.has(this.engineBase());
1203
+ const facetListing = async (page2, limit2) => {
1201
1204
  const { currency } = await this.facts.ready();
1202
1205
  const minor = (n) => Number.isFinite(n) ? Math.max(0, toMinor(n, currency)) : void 0;
1203
- const env2 = await this.engine("/api/products", {
1206
+ return this.engine("/api/products", {
1204
1207
  query: {
1205
1208
  q: search,
1206
- page,
1207
- limit,
1209
+ page: page2,
1210
+ limit: limit2,
1208
1211
  channel: this.creds.channel,
1209
1212
  category: q.category,
1210
1213
  tag: q.tags,
@@ -1217,6 +1220,16 @@ var CatalogService = class extends BaseService {
1217
1220
  facets: 1
1218
1221
  }
1219
1222
  });
1223
+ };
1224
+ if (triedIndex && await this.facts.pluginOn("meilisearch")) {
1225
+ const [found, rail] = await Promise.all([
1226
+ this.fromIndex(q, { page, limit, search, sortKey, wantsRange, hierarchy }),
1227
+ counted ? facetListing(1, 1).catch(() => null) : null
1228
+ ]);
1229
+ if (found) return rail?.meta?.facets ? { ...found, facets: rail.meta.facets } : found;
1230
+ }
1231
+ if (counted) {
1232
+ const env2 = await facetListing(page, limit);
1220
1233
  if (env2.meta?.facets) {
1221
1234
  return {
1222
1235
  ...toPage(env2, (p) => toProduct(p)),
@@ -1227,39 +1240,9 @@ var CatalogService = class extends BaseService {
1227
1240
  }
1228
1241
  legacyListing.add(this.engineBase());
1229
1242
  }
1230
- if ((sortKey || wantsRange) && await this.facts.pluginOn("meilisearch")) {
1231
- const filters = [];
1232
- if (q.category) {
1233
- const names = (await this.facts.subtree(q.category).catch(() => [])).map(
1234
- (c) => quote(c.full_name)
1235
- );
1236
- if (!names.length) return { ...emptyCatalog(limit), categoryHierarchy: hierarchy, sorted: true };
1237
- filters.push(`category IN [${names.join(", ")}]`);
1238
- }
1239
- if (wantsRange) {
1240
- const { currency } = await this.facts.ready();
1241
- if (Number.isFinite(q.priceFrom)) filters.push(`price_minor >= ${toMinor(q.priceFrom, currency)}`);
1242
- if (Number.isFinite(q.priceTo)) filters.push(`price_minor <= ${toMinor(q.priceTo, currency)}`);
1243
- }
1244
- const res = await this.data("/x/meilisearch/search", {
1245
- query: {
1246
- q: search ?? "",
1247
- limit,
1248
- offset: (page - 1) * limit,
1249
- filter: filters.join(" AND ") || void 0,
1250
- sort: sortKey
1251
- }
1252
- });
1253
- const total = res.total ?? 0;
1254
- return {
1255
- data: (res.hits ?? []).map(fromSearchDoc),
1256
- count: total,
1257
- pageSize: limit,
1258
- noOfPage: Math.max(1, Math.ceil(total / limit)),
1259
- page,
1260
- categoryHierarchy: hierarchy,
1261
- sorted: true
1262
- };
1243
+ if (!triedIndex && (sortKey || wantsRange)) {
1244
+ const found = await this.fromIndex(q, { page, limit, search, sortKey, wantsRange, hierarchy });
1245
+ if (found) return found;
1263
1246
  }
1264
1247
  const sorted = !sortKey && !wantsRange;
1265
1248
  if (q.category && !search) {
@@ -1279,6 +1262,45 @@ var CatalogService = class extends BaseService {
1279
1262
  });
1280
1263
  return { ...toPage(env, (p) => toProduct(p)), categoryHierarchy: hierarchy, sorted };
1281
1264
  }
1265
+ /**
1266
+ * The shelf from the Meilisearch module, or null when the store has it off,
1267
+ * sells through a channel, finds nothing for the words, or it cannot answer (Meilisearch down: the engine says `unavailable`), so
1268
+ * the caller asks the database instead of showing the shopper an error.
1269
+ */
1270
+ async fromIndex(q, o) {
1271
+ if (this.creds.channel || !await this.facts.pluginOn("meilisearch")) return null;
1272
+ const filters = [];
1273
+ if (q.category) {
1274
+ const names = (await this.facts.subtree(q.category).catch(() => [])).map((c) => quote(c.full_name));
1275
+ if (!names.length) return { ...emptyCatalog(o.limit), categoryHierarchy: o.hierarchy, sorted: true };
1276
+ filters.push(`category IN [${names.join(", ")}]`);
1277
+ }
1278
+ if (o.wantsRange) {
1279
+ const { currency } = await this.facts.ready();
1280
+ if (Number.isFinite(q.priceFrom)) filters.push(`price_minor >= ${toMinor(q.priceFrom, currency)}`);
1281
+ if (Number.isFinite(q.priceTo)) filters.push(`price_minor <= ${toMinor(q.priceTo, currency)}`);
1282
+ }
1283
+ const res = await this.data("/x/meilisearch/search", {
1284
+ query: {
1285
+ q: o.search ?? "",
1286
+ limit: o.limit,
1287
+ offset: (o.page - 1) * o.limit,
1288
+ filter: filters.join(" AND ") || void 0,
1289
+ sort: o.sortKey
1290
+ }
1291
+ }).catch(() => null);
1292
+ if (!res || res.unavailable || o.search && !res.hits?.length) return null;
1293
+ const total = res.total ?? 0;
1294
+ return {
1295
+ data: (res.hits ?? []).map(fromSearchDoc),
1296
+ count: total,
1297
+ pageSize: o.limit,
1298
+ noOfPage: Math.max(1, Math.ceil(total / o.limit)),
1299
+ page: o.page,
1300
+ categoryHierarchy: o.hierarchy,
1301
+ sorted: true
1302
+ };
1303
+ }
1282
1304
  };
1283
1305
  function emptyCatalog(limit = PAGE_SIZE) {
1284
1306
  return { data: [], count: 0, pageSize: limit, noOfPage: 0, page: 1 };
@@ -1576,10 +1598,8 @@ var MeilisearchService = class extends CatalogService {
1576
1598
  async searchAutoComplete({ query, search, limit = 8 } = {}) {
1577
1599
  const q = (query ?? search ?? "").trim();
1578
1600
  if (!q) return { data: [] };
1579
- if (await this.facts.pluginOn("meilisearch")) {
1580
- const res = await this.data("/x/meilisearch/search", { query: { q, limit } });
1581
- return { data: (res.hits ?? []).map(fromSearchDoc) };
1582
- }
1601
+ const found = await this.fromIndex({}, { page: 1, limit, search: q, wantsRange: false, hierarchy: [] });
1602
+ if (found) return { data: found.data };
1583
1603
  const rows = await this.data("/api/products", { query: { q, limit, channel: this.creds.channel } }) ?? [];
1584
1604
  return { data: rows.map((p) => toProduct(p)) };
1585
1605
  }
@@ -3007,6 +3027,12 @@ var StoreService = class extends BaseService {
3007
3027
  ...profile?.name ? { name: profile.name } : {},
3008
3028
  ...profile?.email ? { businessEmail: profile.email } : {},
3009
3029
  ...profile?.phone ? { businessPhone: profile.phone } : {},
3030
+ // The store's pictures (D148), the same: one it has not chosen keeps the static one.
3031
+ // Its own logo also replaces the static dark logo, which would be the deployment's
3032
+ // mark beside this store's; without a dark one of its own, its logo is the fallback.
3033
+ ...profile?.logo_url ? { logo: mediaUrl(profile.logo_url), logoDark: void 0 } : {},
3034
+ ...profile?.logo_dark_url ? { logoDark: mediaUrl(profile.logo_dark_url) } : {},
3035
+ ...profile?.icon_url ? { favicon: mediaUrl(profile.icon_url) } : {},
3010
3036
  currencyCode: code,
3011
3037
  currencySymbol: sameCurrency ? base.currencySymbol ?? symbolFor(code) : symbolFor(code),
3012
3038
  currency: {
@@ -3029,7 +3055,18 @@ var StoreService = class extends BaseService {
3029
3055
  newsletter: { ...basePlugins.newsletter, ...flag(plugins.has("newsletter")) },
3030
3056
  // Discount codes are core: every GoCommerce store takes them, so
3031
3057
  // the code box is shown whatever the static record says.
3032
- isDiscountCoupons: { ...basePlugins.isDiscountCoupons, active: true }
3058
+ isDiscountCoupons: { ...basePlugins.isDiscountCoupons, active: true },
3059
+ // The engine resizes the store's own pictures in Cloudflare's URL shape
3060
+ // (D139), so the storefront's srcset asks it for each width. Its public
3061
+ // origin, not the proxy path: an <img> needs no CORS, and the proxy would
3062
+ // put every picture through the storefront's server.
3063
+ ...profile?.images_path && this.creds.apiUrl ? {
3064
+ imageCdn: {
3065
+ provider: "cloudflare",
3066
+ url: this.creds.apiUrl.trim().replace(/\/+$/, "") + profile.images_path,
3067
+ active: true
3068
+ }
3069
+ } : {}
3033
3070
  },
3034
3071
  ...menus?.length ? { menu: menus } : {},
3035
3072
  ...deliverable(base, ships),