@porulle/adapter-woocommerce 0.74.1 → 0.74.3
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/dist/catalog.js +53 -10
- package/dist/index.js +1 -1
- package/dist/orders.js +4 -3
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/catalog.ts +55 -10
- package/src/index.ts +1 -1
- package/src/orders.ts +4 -3
package/dist/catalog.js
CHANGED
|
@@ -59,8 +59,49 @@ function status(value) {
|
|
|
59
59
|
return "draft";
|
|
60
60
|
return undefined;
|
|
61
61
|
}
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
/** "pa_color", "Color" and "color" are one attribute; "Blue" and the term slug "blue" one value. */
|
|
63
|
+
function attributeKey(value) {
|
|
64
|
+
return value.trim().toLowerCase().replace(/^pa_/, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The option axes a variable product sells along. WooCommerce lets a variation use a global
|
|
68
|
+
* attribute (`pa_color`) by its term SLUG ("blue") while the product lists the term NAME ("Blue") —
|
|
69
|
+
* or does not list that attribute at all (measured on WooCommerce's own sample catalogue). The axes
|
|
70
|
+
* are the product's declared variation attributes plus any its variations use, and each value is
|
|
71
|
+
* spelled as the product spells it where it declares one, so every variant's values are among them.
|
|
72
|
+
*/
|
|
73
|
+
function optionAxes(product, variations) {
|
|
74
|
+
const axes = new Map();
|
|
75
|
+
for (const attribute of product.attributes.filter((candidate) => candidate.variation === true)) {
|
|
76
|
+
axes.set(attributeKey(attribute.name), { name: attribute.name, values: [...attribute.options] });
|
|
77
|
+
}
|
|
78
|
+
const resolve = (name, option) => {
|
|
79
|
+
const key = attributeKey(name);
|
|
80
|
+
let axis = axes.get(key);
|
|
81
|
+
if (!axis) {
|
|
82
|
+
axis = { name: name.replace(/^pa_/, "").replace(/^./, (first) => first.toUpperCase()), values: [] };
|
|
83
|
+
axes.set(key, axis);
|
|
84
|
+
}
|
|
85
|
+
const declared = axis.values.find((value) => attributeKey(value) === attributeKey(option));
|
|
86
|
+
if (declared)
|
|
87
|
+
return { name: axis.name, value: declared };
|
|
88
|
+
const shown = option.replace(/^./, (first) => first.toUpperCase());
|
|
89
|
+
axis.values.push(shown);
|
|
90
|
+
return { name: axis.name, value: shown };
|
|
91
|
+
};
|
|
92
|
+
for (const row of variations)
|
|
93
|
+
for (const attribute of row.attributes)
|
|
94
|
+
if (attribute.option)
|
|
95
|
+
resolve(attribute.name, attribute.option);
|
|
96
|
+
return { axes: [...axes.values()], resolve };
|
|
97
|
+
}
|
|
98
|
+
function variant(row, currency, resolve) {
|
|
99
|
+
const optionValues = Object.fromEntries(row.attributes.flatMap((attribute) => {
|
|
100
|
+
if (!attribute.option)
|
|
101
|
+
return [];
|
|
102
|
+
const resolved = resolve(attribute.name, attribute.option);
|
|
103
|
+
return [[resolved.name, resolved.value]];
|
|
104
|
+
}));
|
|
64
105
|
const rowPrices = prices(row, currency);
|
|
65
106
|
return {
|
|
66
107
|
externalId: row.id,
|
|
@@ -94,15 +135,17 @@ export function catalogItem(product, variations, currency) {
|
|
|
94
135
|
images.push({ externalId: row.image.id, url: row.image.src, ...(row.image.alt ? { alt: row.image.alt } : {}), role: "gallery", sortOrder: images.length, variantExternalIds: [row.id] });
|
|
95
136
|
}
|
|
96
137
|
}
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
displayName: attribute.name,
|
|
100
|
-
sortOrder: attribute.position ?? index,
|
|
101
|
-
values: attribute.options.map((value, valueIndex) => ({ value, displayValue: value, sortOrder: valueIndex })),
|
|
102
|
-
}));
|
|
138
|
+
const sellable = product.type === "simple" ? [] : variations.filter((row) => row.status !== "private");
|
|
139
|
+
const { axes, resolve } = optionAxes(product, sellable);
|
|
103
140
|
const variants = product.type === "simple"
|
|
104
|
-
? [variant({ id: product.id, sku: product.sku, price: product.price, regular_price: product.regular_price, sale_price: product.sale_price, attributes: [] }, currency)]
|
|
105
|
-
:
|
|
141
|
+
? [variant({ id: product.id, sku: product.sku, price: product.price, regular_price: product.regular_price, sale_price: product.sale_price, attributes: [] }, currency, resolve)]
|
|
142
|
+
: sellable.map((row) => variant(row, currency, resolve));
|
|
143
|
+
const options = product.type === "simple" ? [] : axes.map((axis, index) => ({
|
|
144
|
+
name: axis.name,
|
|
145
|
+
displayName: axis.name,
|
|
146
|
+
sortOrder: index,
|
|
147
|
+
values: axis.values.map((value, valueIndex) => ({ value, displayValue: value, sortOrder: valueIndex })),
|
|
148
|
+
}));
|
|
106
149
|
const itemStatus = status(product.status);
|
|
107
150
|
return {
|
|
108
151
|
externalId: product.id,
|
package/dist/index.js
CHANGED
|
@@ -38,7 +38,7 @@ export function wooConnector(options = {}) {
|
|
|
38
38
|
async probeStore(input) {
|
|
39
39
|
const storeDomain = normalizeStoreDomain(input, { allowPrivateHosts: transport.allowPrivateHosts });
|
|
40
40
|
if (!storeDomain) {
|
|
41
|
-
const why =
|
|
41
|
+
const why = refusedStoreUrl(input);
|
|
42
42
|
return refused(why === "http" ? "WOO_STORE_NOT_HTTPS" : "WOO_INVALID_STORE_DOMAIN", why === "http"
|
|
43
43
|
? "Your store must be served over https (with a valid certificate) to connect."
|
|
44
44
|
: why === "private_host" ? "That address is not a public website." : `"${input}" is not a store address. Enter it like https://shop.example.com.`);
|
package/dist/orders.js
CHANGED
|
@@ -142,9 +142,10 @@ export async function pushOrder(client, slice, options) {
|
|
|
142
142
|
// A refusal can leave a checkout-draft behind; it is ours, so it goes.
|
|
143
143
|
if (draftId?.success)
|
|
144
144
|
await client.send("DELETE", `/wc/v3/orders/${draftId.data}`, undefined, z.unknown(), { force: "true" });
|
|
145
|
-
// A 4xx is the store's answer. Anything else
|
|
146
|
-
// order: retriable, and the next
|
|
147
|
-
|
|
145
|
+
// A 4xx WooCommerce explained (its JSON error) is the store's answer. Anything else — a timeout,
|
|
146
|
+
// a 5xx, a firewall's page — may or may not have created the order: retriable, and the next
|
|
147
|
+
// attempt looks for it before creating anything.
|
|
148
|
+
const answered = error.status !== undefined && error.status >= 400 && error.status < 500 && error.body !== undefined;
|
|
148
149
|
return Err({ code: answered ? "WOO_ORDER_REJECTED" : error.code, message: answered ? `WooCommerce refused the order: ${error.message}` : `The store's answer to the order was lost (${error.message}); it is looked for before trying again.`, retriable: !answered });
|
|
149
150
|
}
|
|
150
151
|
const order = created.value;
|