@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/index.cjs
CHANGED
|
@@ -1086,11 +1086,14 @@ var StoreFacts = class extends BaseService {
|
|
|
1086
1086
|
|
|
1087
1087
|
// src/services/catalog.ts
|
|
1088
1088
|
var PAGE_SIZE = 24;
|
|
1089
|
-
function
|
|
1090
|
-
if (!sort) return void 0;
|
|
1089
|
+
function sortParts(sort) {
|
|
1091
1090
|
const s = sort.trim();
|
|
1092
1091
|
const [field, dir] = s.startsWith("-") ? [s.slice(1), "desc"] : s.includes(":") ? s.split(":") : [s, "asc"];
|
|
1093
|
-
|
|
1092
|
+
return [field, dir === "desc" ? "desc" : "asc"];
|
|
1093
|
+
}
|
|
1094
|
+
function indexSort(sort) {
|
|
1095
|
+
if (!sort) return void 0;
|
|
1096
|
+
const [field, d] = sortParts(sort);
|
|
1094
1097
|
switch (field) {
|
|
1095
1098
|
case "price":
|
|
1096
1099
|
return `price_minor:${d}`;
|
|
@@ -1106,6 +1109,20 @@ function indexSort(sort) {
|
|
|
1106
1109
|
return void 0;
|
|
1107
1110
|
}
|
|
1108
1111
|
}
|
|
1112
|
+
function engineSort(sort) {
|
|
1113
|
+
if (!sort || NATURAL.has(sort)) return {};
|
|
1114
|
+
const [field, order] = sortParts(sort);
|
|
1115
|
+
const key = {
|
|
1116
|
+
price: "price",
|
|
1117
|
+
createdAt: "created_at",
|
|
1118
|
+
created_at: "created_at",
|
|
1119
|
+
updatedAt: "updated_at",
|
|
1120
|
+
updated_at: "updated_at",
|
|
1121
|
+
title: "title",
|
|
1122
|
+
name: "title"
|
|
1123
|
+
}[field];
|
|
1124
|
+
return key ? { sort: key, order } : {};
|
|
1125
|
+
}
|
|
1109
1126
|
var NATURAL = /* @__PURE__ */ new Set([
|
|
1110
1127
|
"-createdAt",
|
|
1111
1128
|
"createdAt:desc",
|
|
@@ -1115,6 +1132,7 @@ var NATURAL = /* @__PURE__ */ new Set([
|
|
|
1115
1132
|
"popularity:desc"
|
|
1116
1133
|
]);
|
|
1117
1134
|
var quote = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
1135
|
+
var legacyListing = /* @__PURE__ */ new Set();
|
|
1118
1136
|
var CatalogService = class extends BaseService {
|
|
1119
1137
|
facts = new StoreFacts(this._fetch);
|
|
1120
1138
|
async queryCatalog(q) {
|
|
@@ -1124,6 +1142,39 @@ var CatalogService = class extends BaseService {
|
|
|
1124
1142
|
const wantsRange = Number.isFinite(q.priceFrom) || Number.isFinite(q.priceTo);
|
|
1125
1143
|
const sortKey = q.sort && !NATURAL.has(q.sort) ? indexSort(q.sort) : void 0;
|
|
1126
1144
|
const hierarchy = q.category ? await this.facts.hierarchy(q.category) : [];
|
|
1145
|
+
const attr = Object.entries(q.attributes ?? {}).flatMap(
|
|
1146
|
+
([handle, values]) => values.filter(Boolean).map((v) => `${handle}:${v}`)
|
|
1147
|
+
);
|
|
1148
|
+
if (q.facets && !legacyListing.has(this.engineBase())) {
|
|
1149
|
+
const { currency } = await this.facts.ready();
|
|
1150
|
+
const minor = (n) => Number.isFinite(n) ? Math.max(0, toMinor(n, currency)) : void 0;
|
|
1151
|
+
const env2 = await this.engine("/api/products", {
|
|
1152
|
+
query: {
|
|
1153
|
+
q: search,
|
|
1154
|
+
page,
|
|
1155
|
+
limit,
|
|
1156
|
+
channel: this.creds.channel,
|
|
1157
|
+
category: q.category,
|
|
1158
|
+
tag: q.tags,
|
|
1159
|
+
vendor: q.vendors,
|
|
1160
|
+
product_type: q.productTypes,
|
|
1161
|
+
attr,
|
|
1162
|
+
price_min: minor(q.priceFrom),
|
|
1163
|
+
price_max: minor(q.priceTo),
|
|
1164
|
+
...engineSort(q.sort),
|
|
1165
|
+
facets: 1
|
|
1166
|
+
}
|
|
1167
|
+
});
|
|
1168
|
+
if (env2.meta?.facets) {
|
|
1169
|
+
return {
|
|
1170
|
+
...toPage(env2, (p) => toProduct(p)),
|
|
1171
|
+
categoryHierarchy: hierarchy,
|
|
1172
|
+
sorted: true,
|
|
1173
|
+
facets: env2.meta.facets
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
legacyListing.add(this.engineBase());
|
|
1177
|
+
}
|
|
1127
1178
|
if ((sortKey || wantsRange) && await this.facts.pluginOn("meilisearch")) {
|
|
1128
1179
|
const filters = [];
|
|
1129
1180
|
if (q.category) {
|
|
@@ -1171,9 +1222,6 @@ var CatalogService = class extends BaseService {
|
|
|
1171
1222
|
sorted
|
|
1172
1223
|
};
|
|
1173
1224
|
}
|
|
1174
|
-
const attr = Object.entries(q.attributes ?? {}).flatMap(
|
|
1175
|
-
([handle, values]) => values.filter(Boolean).map((v) => `${handle}:${v}`)
|
|
1176
|
-
);
|
|
1177
1225
|
const env = await this.engine("/api/products", {
|
|
1178
1226
|
query: { q: search, page, limit, channel: this.creds.channel, attr }
|
|
1179
1227
|
});
|
|
@@ -1339,16 +1387,44 @@ var num = (v) => {
|
|
|
1339
1387
|
const n = Number(v);
|
|
1340
1388
|
return Number.isFinite(n) ? n : void 0;
|
|
1341
1389
|
};
|
|
1390
|
+
function decodeOnce(v) {
|
|
1391
|
+
try {
|
|
1392
|
+
return decodeURIComponent(v);
|
|
1393
|
+
} catch {
|
|
1394
|
+
return v;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
function listParam(p, key) {
|
|
1398
|
+
const out = p.getAll(key).flatMap((v) => decodeOnce(v).split(",")).map((v) => v.trim()).filter(Boolean);
|
|
1399
|
+
return out.length ? out : void 0;
|
|
1400
|
+
}
|
|
1342
1401
|
function attributesFrom(p) {
|
|
1343
1402
|
const out = {};
|
|
1344
|
-
for (const
|
|
1403
|
+
for (const key of new Set(p.keys())) {
|
|
1345
1404
|
if (!key.startsWith("attributes.")) continue;
|
|
1346
1405
|
const handle = key.slice("attributes.".length);
|
|
1347
|
-
const values =
|
|
1348
|
-
if (handle && values
|
|
1406
|
+
const values = listParam(p, key);
|
|
1407
|
+
if (handle && values) out[handle] = values;
|
|
1349
1408
|
}
|
|
1350
1409
|
return Object.keys(out).length ? out : void 0;
|
|
1351
1410
|
}
|
|
1411
|
+
var counts = (values) => Object.fromEntries((values ?? []).map((v) => [v.value, v.count]));
|
|
1412
|
+
function toStorefrontFacets(f) {
|
|
1413
|
+
if (!f) return { priceStat: {}, categories: [], tags: [], allFilters: {} };
|
|
1414
|
+
const allFilters = {};
|
|
1415
|
+
const group = (key, values) => {
|
|
1416
|
+
if (values?.length) allFilters[key] = counts(values);
|
|
1417
|
+
};
|
|
1418
|
+
group("vendor", f.vendors);
|
|
1419
|
+
group("product_type", f.product_types);
|
|
1420
|
+
for (const [handle, values] of Object.entries(f.attributes ?? {})) group(`attributes.${handle}`, values);
|
|
1421
|
+
return {
|
|
1422
|
+
priceStat: f.price ? { min: toMajor(f.price.min), max: toMajor(f.price.max) } : {},
|
|
1423
|
+
categories: (f.categories ?? []).map((c) => ({ name: c.title, slug: c.slug, count: c.count })),
|
|
1424
|
+
tags: (f.tags ?? []).map((t) => ({ name: t.value, count: t.count })),
|
|
1425
|
+
allFilters
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1352
1428
|
var SearchService = class extends CatalogService {
|
|
1353
1429
|
emptyResult() {
|
|
1354
1430
|
return {
|
|
@@ -1356,7 +1432,7 @@ var SearchService = class extends CatalogService {
|
|
|
1356
1432
|
count: 0,
|
|
1357
1433
|
totalPages: 0,
|
|
1358
1434
|
categoryHierarchy: [],
|
|
1359
|
-
facets:
|
|
1435
|
+
facets: toStorefrontFacets()
|
|
1360
1436
|
};
|
|
1361
1437
|
}
|
|
1362
1438
|
async searchWithUrl(url, slug) {
|
|
@@ -1371,7 +1447,11 @@ var SearchService = class extends CatalogService {
|
|
|
1371
1447
|
category: slug || p.get("categories")?.split(",")[0] || void 0,
|
|
1372
1448
|
priceFrom: num(p.get("priceFrom")) ?? num(from ?? null),
|
|
1373
1449
|
priceTo: num(p.get("priceTo")) ?? num(to ?? null),
|
|
1374
|
-
attributes: attributesFrom(p)
|
|
1450
|
+
attributes: attributesFrom(p),
|
|
1451
|
+
tags: listParam(p, "tags"),
|
|
1452
|
+
vendors: listParam(p, "vendor"),
|
|
1453
|
+
productTypes: listParam(p, "product_type"),
|
|
1454
|
+
facets: true
|
|
1375
1455
|
});
|
|
1376
1456
|
return {
|
|
1377
1457
|
data: res.data,
|
|
@@ -1379,7 +1459,7 @@ var SearchService = class extends CatalogService {
|
|
|
1379
1459
|
totalPages: res.noOfPage,
|
|
1380
1460
|
page: res.page,
|
|
1381
1461
|
categoryHierarchy: res.categoryHierarchy,
|
|
1382
|
-
facets:
|
|
1462
|
+
facets: toStorefrontFacets(res.facets)
|
|
1383
1463
|
};
|
|
1384
1464
|
} catch (err) {
|
|
1385
1465
|
if (err?.status === 404) return this.emptyResult();
|
|
@@ -2599,21 +2679,38 @@ var MenuService = class extends BaseService {
|
|
|
2599
2679
|
const m = await this.data(`/x/navigation/menus/${encodeURIComponent(handle)}`);
|
|
2600
2680
|
return { menuId: m.handle, name: m.title, items: (m.items ?? []).map(toItem) };
|
|
2601
2681
|
}
|
|
2602
|
-
/**
|
|
2682
|
+
/**
|
|
2683
|
+
* The header and footer menus, or `null` when the module is not there. Cached: a menu is
|
|
2684
|
+
* store-level. Each is the menu the shop chose for it in the theme editor's header or footer
|
|
2685
|
+
* (GoCommerce D131), still under the `header` / `footer` id every storefront looks for, so the
|
|
2686
|
+
* choice reaches every place that draws one; the menus named so are the default.
|
|
2687
|
+
*/
|
|
2603
2688
|
async engineMenus() {
|
|
2604
2689
|
if (!await this.facts.pluginOn("navigation")) return null;
|
|
2605
2690
|
return remember(`${this.engineBase()}|${this.store ?? ""}|menus`, async () => {
|
|
2606
2691
|
const out = [];
|
|
2607
|
-
for (const
|
|
2692
|
+
for (const part of ["header", "footer"]) {
|
|
2608
2693
|
try {
|
|
2609
|
-
const
|
|
2610
|
-
|
|
2694
|
+
const handle = await this.chosenMenu(part);
|
|
2695
|
+
const m = await this.data(`/x/navigation/menus/${encodeURIComponent(handle)}`);
|
|
2696
|
+
out.push({ menuId: part, name: m.title, items: (m.items ?? []).map(toItem) });
|
|
2611
2697
|
} catch {
|
|
2612
2698
|
}
|
|
2613
2699
|
}
|
|
2614
2700
|
return out;
|
|
2615
2701
|
}).catch(() => null);
|
|
2616
2702
|
}
|
|
2703
|
+
/** The handle of the menu the published header or footer names, else the part's own name. */
|
|
2704
|
+
async chosenMenu(part) {
|
|
2705
|
+
if (!await this.facts.pluginOn("layouts")) return part;
|
|
2706
|
+
try {
|
|
2707
|
+
const layout = await this.data(`/x/layouts/${part}`);
|
|
2708
|
+
const menu = layout?.sections?.find((s) => s.type === part)?.settings?.menu;
|
|
2709
|
+
return typeof menu === "string" && menu ? menu : part;
|
|
2710
|
+
} catch {
|
|
2711
|
+
return part;
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2617
2714
|
};
|
|
2618
2715
|
var menuService = new MenuService();
|
|
2619
2716
|
|
|
@@ -2728,7 +2825,10 @@ var StoreService = class extends BaseService {
|
|
|
2728
2825
|
isProductReviewsAndRatings: {
|
|
2729
2826
|
...basePlugins.isProductReviewsAndRatings,
|
|
2730
2827
|
...flag(plugins.has("product-reviews"))
|
|
2731
|
-
}
|
|
2828
|
+
},
|
|
2829
|
+
// The footer's sign-up box posts to ext/newsletter, and the theme editor's footer
|
|
2830
|
+
// switch (D131) can only show it while that is on.
|
|
2831
|
+
newsletter: { ...basePlugins.newsletter, ...flag(plugins.has("newsletter")) }
|
|
2732
2832
|
},
|
|
2733
2833
|
...menus?.length ? { menu: menus } : {},
|
|
2734
2834
|
...deliverable(base, ships),
|
|
@@ -2843,6 +2943,35 @@ var LayoutService = class extends BaseService {
|
|
|
2843
2943
|
return null;
|
|
2844
2944
|
}
|
|
2845
2945
|
}
|
|
2946
|
+
/**
|
|
2947
|
+
* The header the shop set in the theme editor (D131), drawn on every
|
|
2948
|
+
* page: the published one, or a preview key's draft. Null — the
|
|
2949
|
+
* storefront's own header — when none is published, the editor is off, the
|
|
2950
|
+
* engine is older, or anything fails: a header is never worth an error page.
|
|
2951
|
+
*/
|
|
2952
|
+
async getHeader({ preview } = {}) {
|
|
2953
|
+
return this.chrome("header", preview);
|
|
2954
|
+
}
|
|
2955
|
+
/** The footer the shop set in the theme editor (D131); null for the storefront's own, as getHeader. */
|
|
2956
|
+
async getFooter({ preview } = {}) {
|
|
2957
|
+
return this.chrome("footer", preview);
|
|
2958
|
+
}
|
|
2959
|
+
async chrome(page, preview) {
|
|
2960
|
+
let layout;
|
|
2961
|
+
try {
|
|
2962
|
+
layout = await this.getPageLayout(page, { preview });
|
|
2963
|
+
} catch {
|
|
2964
|
+
return null;
|
|
2965
|
+
}
|
|
2966
|
+
const section = layout?.sections.find((s) => s.type === page);
|
|
2967
|
+
if (!layout || !section) return null;
|
|
2968
|
+
const settings = { ...section.settings };
|
|
2969
|
+
let menu = null;
|
|
2970
|
+
if (typeof settings.menu === "string" && settings.menu) {
|
|
2971
|
+
menu = await new MenuService(this._fetch).getMenu(settings.menu).catch(() => null);
|
|
2972
|
+
}
|
|
2973
|
+
return { revision: layout.revision, preview: layout.preview, settings, menu };
|
|
2974
|
+
}
|
|
2846
2975
|
};
|
|
2847
2976
|
function tokens(v) {
|
|
2848
2977
|
return v && typeof v === "object" && !Array.isArray(v) && Object.keys(v).length ? { ...v } : null;
|
|
@@ -3540,6 +3669,7 @@ exports.toOrder = toOrder;
|
|
|
3540
3669
|
exports.toPage = toPage;
|
|
3541
3670
|
exports.toProduct = toProduct;
|
|
3542
3671
|
exports.toRating = toRating;
|
|
3672
|
+
exports.toStorefrontFacets = toStorefrontFacets;
|
|
3543
3673
|
exports.toUser = toUser;
|
|
3544
3674
|
exports.toVariant = toVariant;
|
|
3545
3675
|
exports.toVendor = toVendor;
|