@misiki/gocommerce-connector 0.9.1 → 0.11.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 +5 -4
- package/dist/{index-DSvV8FbK.d.cts → index-90wdO5MM.d.cts} +182 -29
- package/dist/{index-DSvV8FbK.d.ts → index-90wdO5MM.d.ts} +182 -29
- package/dist/index.cjs +147 -17
- 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 +147 -18
- package/dist/index.js.map +1 -1
- package/dist/services/index.cjs +147 -17
- 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 +147 -18
- package/dist/services/index.js.map +1 -1
- package/package.json +1 -1
package/dist/services/index.cjs
CHANGED
|
@@ -1067,11 +1067,14 @@ var StoreFacts = class extends BaseService {
|
|
|
1067
1067
|
|
|
1068
1068
|
// src/services/catalog.ts
|
|
1069
1069
|
var PAGE_SIZE = 24;
|
|
1070
|
-
function
|
|
1071
|
-
if (!sort) return void 0;
|
|
1070
|
+
function sortParts(sort) {
|
|
1072
1071
|
const s = sort.trim();
|
|
1073
1072
|
const [field, dir] = s.startsWith("-") ? [s.slice(1), "desc"] : s.includes(":") ? s.split(":") : [s, "asc"];
|
|
1074
|
-
|
|
1073
|
+
return [field, dir === "desc" ? "desc" : "asc"];
|
|
1074
|
+
}
|
|
1075
|
+
function indexSort(sort) {
|
|
1076
|
+
if (!sort) return void 0;
|
|
1077
|
+
const [field, d] = sortParts(sort);
|
|
1075
1078
|
switch (field) {
|
|
1076
1079
|
case "price":
|
|
1077
1080
|
return `price_minor:${d}`;
|
|
@@ -1087,6 +1090,20 @@ function indexSort(sort) {
|
|
|
1087
1090
|
return void 0;
|
|
1088
1091
|
}
|
|
1089
1092
|
}
|
|
1093
|
+
function engineSort(sort) {
|
|
1094
|
+
if (!sort || NATURAL.has(sort)) return {};
|
|
1095
|
+
const [field, order] = sortParts(sort);
|
|
1096
|
+
const key = {
|
|
1097
|
+
price: "price",
|
|
1098
|
+
createdAt: "created_at",
|
|
1099
|
+
created_at: "created_at",
|
|
1100
|
+
updatedAt: "updated_at",
|
|
1101
|
+
updated_at: "updated_at",
|
|
1102
|
+
title: "title",
|
|
1103
|
+
name: "title"
|
|
1104
|
+
}[field];
|
|
1105
|
+
return key ? { sort: key, order } : {};
|
|
1106
|
+
}
|
|
1090
1107
|
var NATURAL = /* @__PURE__ */ new Set([
|
|
1091
1108
|
"-createdAt",
|
|
1092
1109
|
"createdAt:desc",
|
|
@@ -1096,6 +1113,7 @@ var NATURAL = /* @__PURE__ */ new Set([
|
|
|
1096
1113
|
"popularity:desc"
|
|
1097
1114
|
]);
|
|
1098
1115
|
var quote = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
1116
|
+
var legacyListing = /* @__PURE__ */ new Set();
|
|
1099
1117
|
var CatalogService = class extends BaseService {
|
|
1100
1118
|
facts = new StoreFacts(this._fetch);
|
|
1101
1119
|
async queryCatalog(q) {
|
|
@@ -1105,6 +1123,39 @@ var CatalogService = class extends BaseService {
|
|
|
1105
1123
|
const wantsRange = Number.isFinite(q.priceFrom) || Number.isFinite(q.priceTo);
|
|
1106
1124
|
const sortKey = q.sort && !NATURAL.has(q.sort) ? indexSort(q.sort) : void 0;
|
|
1107
1125
|
const hierarchy = q.category ? await this.facts.hierarchy(q.category) : [];
|
|
1126
|
+
const attr = Object.entries(q.attributes ?? {}).flatMap(
|
|
1127
|
+
([handle, values]) => values.filter(Boolean).map((v) => `${handle}:${v}`)
|
|
1128
|
+
);
|
|
1129
|
+
if (q.facets && !legacyListing.has(this.engineBase())) {
|
|
1130
|
+
const { currency } = await this.facts.ready();
|
|
1131
|
+
const minor = (n) => Number.isFinite(n) ? Math.max(0, toMinor(n, currency)) : void 0;
|
|
1132
|
+
const env2 = await this.engine("/api/products", {
|
|
1133
|
+
query: {
|
|
1134
|
+
q: search,
|
|
1135
|
+
page,
|
|
1136
|
+
limit,
|
|
1137
|
+
channel: this.creds.channel,
|
|
1138
|
+
category: q.category,
|
|
1139
|
+
tag: q.tags,
|
|
1140
|
+
vendor: q.vendors,
|
|
1141
|
+
product_type: q.productTypes,
|
|
1142
|
+
attr,
|
|
1143
|
+
price_min: minor(q.priceFrom),
|
|
1144
|
+
price_max: minor(q.priceTo),
|
|
1145
|
+
...engineSort(q.sort),
|
|
1146
|
+
facets: 1
|
|
1147
|
+
}
|
|
1148
|
+
});
|
|
1149
|
+
if (env2.meta?.facets) {
|
|
1150
|
+
return {
|
|
1151
|
+
...toPage(env2, (p) => toProduct(p)),
|
|
1152
|
+
categoryHierarchy: hierarchy,
|
|
1153
|
+
sorted: true,
|
|
1154
|
+
facets: env2.meta.facets
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
legacyListing.add(this.engineBase());
|
|
1158
|
+
}
|
|
1108
1159
|
if ((sortKey || wantsRange) && await this.facts.pluginOn("meilisearch")) {
|
|
1109
1160
|
const filters = [];
|
|
1110
1161
|
if (q.category) {
|
|
@@ -1152,9 +1203,6 @@ var CatalogService = class extends BaseService {
|
|
|
1152
1203
|
sorted
|
|
1153
1204
|
};
|
|
1154
1205
|
}
|
|
1155
|
-
const attr = Object.entries(q.attributes ?? {}).flatMap(
|
|
1156
|
-
([handle, values]) => values.filter(Boolean).map((v) => `${handle}:${v}`)
|
|
1157
|
-
);
|
|
1158
1206
|
const env = await this.engine("/api/products", {
|
|
1159
1207
|
query: { q: search, page, limit, channel: this.creds.channel, attr }
|
|
1160
1208
|
});
|
|
@@ -1320,16 +1368,44 @@ var num = (v) => {
|
|
|
1320
1368
|
const n = Number(v);
|
|
1321
1369
|
return Number.isFinite(n) ? n : void 0;
|
|
1322
1370
|
};
|
|
1371
|
+
function decodeOnce(v) {
|
|
1372
|
+
try {
|
|
1373
|
+
return decodeURIComponent(v);
|
|
1374
|
+
} catch {
|
|
1375
|
+
return v;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
function listParam(p, key) {
|
|
1379
|
+
const out = p.getAll(key).flatMap((v) => decodeOnce(v).split(",")).map((v) => v.trim()).filter(Boolean);
|
|
1380
|
+
return out.length ? out : void 0;
|
|
1381
|
+
}
|
|
1323
1382
|
function attributesFrom(p) {
|
|
1324
1383
|
const out = {};
|
|
1325
|
-
for (const
|
|
1384
|
+
for (const key of new Set(p.keys())) {
|
|
1326
1385
|
if (!key.startsWith("attributes.")) continue;
|
|
1327
1386
|
const handle = key.slice("attributes.".length);
|
|
1328
|
-
const values =
|
|
1329
|
-
if (handle && values
|
|
1387
|
+
const values = listParam(p, key);
|
|
1388
|
+
if (handle && values) out[handle] = values;
|
|
1330
1389
|
}
|
|
1331
1390
|
return Object.keys(out).length ? out : void 0;
|
|
1332
1391
|
}
|
|
1392
|
+
var counts = (values) => Object.fromEntries((values ?? []).map((v) => [v.value, v.count]));
|
|
1393
|
+
function toStorefrontFacets(f) {
|
|
1394
|
+
if (!f) return { priceStat: {}, categories: [], tags: [], allFilters: {} };
|
|
1395
|
+
const allFilters = {};
|
|
1396
|
+
const group = (key, values) => {
|
|
1397
|
+
if (values?.length) allFilters[key] = counts(values);
|
|
1398
|
+
};
|
|
1399
|
+
group("vendor", f.vendors);
|
|
1400
|
+
group("product_type", f.product_types);
|
|
1401
|
+
for (const [handle, values] of Object.entries(f.attributes ?? {})) group(`attributes.${handle}`, values);
|
|
1402
|
+
return {
|
|
1403
|
+
priceStat: f.price ? { min: toMajor(f.price.min), max: toMajor(f.price.max) } : {},
|
|
1404
|
+
categories: (f.categories ?? []).map((c) => ({ name: c.title, slug: c.slug, count: c.count })),
|
|
1405
|
+
tags: (f.tags ?? []).map((t) => ({ name: t.value, count: t.count })),
|
|
1406
|
+
allFilters
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1333
1409
|
var SearchService = class extends CatalogService {
|
|
1334
1410
|
emptyResult() {
|
|
1335
1411
|
return {
|
|
@@ -1337,7 +1413,7 @@ var SearchService = class extends CatalogService {
|
|
|
1337
1413
|
count: 0,
|
|
1338
1414
|
totalPages: 0,
|
|
1339
1415
|
categoryHierarchy: [],
|
|
1340
|
-
facets:
|
|
1416
|
+
facets: toStorefrontFacets()
|
|
1341
1417
|
};
|
|
1342
1418
|
}
|
|
1343
1419
|
async searchWithUrl(url, slug) {
|
|
@@ -1352,7 +1428,11 @@ var SearchService = class extends CatalogService {
|
|
|
1352
1428
|
category: slug || p.get("categories")?.split(",")[0] || void 0,
|
|
1353
1429
|
priceFrom: num(p.get("priceFrom")) ?? num(from ?? null),
|
|
1354
1430
|
priceTo: num(p.get("priceTo")) ?? num(to ?? null),
|
|
1355
|
-
attributes: attributesFrom(p)
|
|
1431
|
+
attributes: attributesFrom(p),
|
|
1432
|
+
tags: listParam(p, "tags"),
|
|
1433
|
+
vendors: listParam(p, "vendor"),
|
|
1434
|
+
productTypes: listParam(p, "product_type"),
|
|
1435
|
+
facets: true
|
|
1356
1436
|
});
|
|
1357
1437
|
return {
|
|
1358
1438
|
data: res.data,
|
|
@@ -1360,7 +1440,7 @@ var SearchService = class extends CatalogService {
|
|
|
1360
1440
|
totalPages: res.noOfPage,
|
|
1361
1441
|
page: res.page,
|
|
1362
1442
|
categoryHierarchy: res.categoryHierarchy,
|
|
1363
|
-
facets:
|
|
1443
|
+
facets: toStorefrontFacets(res.facets)
|
|
1364
1444
|
};
|
|
1365
1445
|
} catch (err) {
|
|
1366
1446
|
if (err?.status === 404) return this.emptyResult();
|
|
@@ -2580,21 +2660,38 @@ var MenuService = class extends BaseService {
|
|
|
2580
2660
|
const m = await this.data(`/x/navigation/menus/${encodeURIComponent(handle)}`);
|
|
2581
2661
|
return { menuId: m.handle, name: m.title, items: (m.items ?? []).map(toItem) };
|
|
2582
2662
|
}
|
|
2583
|
-
/**
|
|
2663
|
+
/**
|
|
2664
|
+
* The header and footer menus, or `null` when the module is not there. Cached: a menu is
|
|
2665
|
+
* store-level. Each is the menu the shop chose for it in the theme editor's header or footer
|
|
2666
|
+
* (GoCommerce D131), still under the `header` / `footer` id every storefront looks for, so the
|
|
2667
|
+
* choice reaches every place that draws one; the menus named so are the default.
|
|
2668
|
+
*/
|
|
2584
2669
|
async engineMenus() {
|
|
2585
2670
|
if (!await this.facts.pluginOn("navigation")) return null;
|
|
2586
2671
|
return remember(`${this.engineBase()}|${this.store ?? ""}|menus`, async () => {
|
|
2587
2672
|
const out = [];
|
|
2588
|
-
for (const
|
|
2673
|
+
for (const part of ["header", "footer"]) {
|
|
2589
2674
|
try {
|
|
2590
|
-
const
|
|
2591
|
-
|
|
2675
|
+
const handle = await this.chosenMenu(part);
|
|
2676
|
+
const m = await this.data(`/x/navigation/menus/${encodeURIComponent(handle)}`);
|
|
2677
|
+
out.push({ menuId: part, name: m.title, items: (m.items ?? []).map(toItem) });
|
|
2592
2678
|
} catch {
|
|
2593
2679
|
}
|
|
2594
2680
|
}
|
|
2595
2681
|
return out;
|
|
2596
2682
|
}).catch(() => null);
|
|
2597
2683
|
}
|
|
2684
|
+
/** The handle of the menu the published header or footer names, else the part's own name. */
|
|
2685
|
+
async chosenMenu(part) {
|
|
2686
|
+
if (!await this.facts.pluginOn("layouts")) return part;
|
|
2687
|
+
try {
|
|
2688
|
+
const layout = await this.data(`/x/layouts/${part}`);
|
|
2689
|
+
const menu = layout?.sections?.find((s) => s.type === part)?.settings?.menu;
|
|
2690
|
+
return typeof menu === "string" && menu ? menu : part;
|
|
2691
|
+
} catch {
|
|
2692
|
+
return part;
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2598
2695
|
};
|
|
2599
2696
|
var menuService = new MenuService();
|
|
2600
2697
|
var readStaticStore = async () => {
|
|
@@ -2712,7 +2809,10 @@ var StoreService = class extends BaseService {
|
|
|
2712
2809
|
isProductReviewsAndRatings: {
|
|
2713
2810
|
...basePlugins.isProductReviewsAndRatings,
|
|
2714
2811
|
...flag(plugins.has("product-reviews"))
|
|
2715
|
-
}
|
|
2812
|
+
},
|
|
2813
|
+
// The footer's sign-up box posts to ext/newsletter, and the theme editor's footer
|
|
2814
|
+
// switch (D131) can only show it while that is on.
|
|
2815
|
+
newsletter: { ...basePlugins.newsletter, ...flag(plugins.has("newsletter")) }
|
|
2716
2816
|
},
|
|
2717
2817
|
...menus?.length ? { menu: menus } : {},
|
|
2718
2818
|
...deliverable(base, ships),
|
|
@@ -2827,6 +2927,35 @@ var LayoutService = class extends BaseService {
|
|
|
2827
2927
|
return null;
|
|
2828
2928
|
}
|
|
2829
2929
|
}
|
|
2930
|
+
/**
|
|
2931
|
+
* The header the shop set in the theme editor (D131), drawn on every
|
|
2932
|
+
* page: the published one, or a preview key's draft. Null — the
|
|
2933
|
+
* storefront's own header — when none is published, the editor is off, the
|
|
2934
|
+
* engine is older, or anything fails: a header is never worth an error page.
|
|
2935
|
+
*/
|
|
2936
|
+
async getHeader({ preview } = {}) {
|
|
2937
|
+
return this.chrome("header", preview);
|
|
2938
|
+
}
|
|
2939
|
+
/** The footer the shop set in the theme editor (D131); null for the storefront's own, as getHeader. */
|
|
2940
|
+
async getFooter({ preview } = {}) {
|
|
2941
|
+
return this.chrome("footer", preview);
|
|
2942
|
+
}
|
|
2943
|
+
async chrome(page, preview) {
|
|
2944
|
+
let layout;
|
|
2945
|
+
try {
|
|
2946
|
+
layout = await this.getPageLayout(page, { preview });
|
|
2947
|
+
} catch {
|
|
2948
|
+
return null;
|
|
2949
|
+
}
|
|
2950
|
+
const section = layout?.sections.find((s) => s.type === page);
|
|
2951
|
+
if (!layout || !section) return null;
|
|
2952
|
+
const settings = { ...section.settings };
|
|
2953
|
+
let menu = null;
|
|
2954
|
+
if (typeof settings.menu === "string" && settings.menu) {
|
|
2955
|
+
menu = await new MenuService(this._fetch).getMenu(settings.menu).catch(() => null);
|
|
2956
|
+
}
|
|
2957
|
+
return { revision: layout.revision, preview: layout.preview, settings, menu };
|
|
2958
|
+
}
|
|
2830
2959
|
};
|
|
2831
2960
|
function tokens(v) {
|
|
2832
2961
|
return v && typeof v === "object" && !Array.isArray(v) && Object.keys(v).length ? { ...v } : null;
|
|
@@ -3496,6 +3625,7 @@ exports.staticStore = staticStore;
|
|
|
3496
3625
|
exports.storeForHost = storeForHost;
|
|
3497
3626
|
exports.storeService = storeService;
|
|
3498
3627
|
exports.toPage = toPage;
|
|
3628
|
+
exports.toStorefrontFacets = toStorefrontFacets;
|
|
3499
3629
|
exports.uploadService = uploadService;
|
|
3500
3630
|
exports.userService = userService;
|
|
3501
3631
|
exports.variantService = variantService;
|