@porulle/import-woocommerce 0.64.0 → 0.65.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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -6
- package/package.json +2 -2
- package/src/index.ts +72 -12
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Err, Ok } from "@porulle/core";
|
|
1
|
+
import { Err, Ok, toMinorUnits } from "@porulle/core";
|
|
2
2
|
function slugify(value) {
|
|
3
3
|
return value
|
|
4
4
|
.toLowerCase()
|
|
@@ -6,13 +6,34 @@ function slugify(value) {
|
|
|
6
6
|
.replace(/[^a-z0-9]+/g, "-")
|
|
7
7
|
.replace(/^-+|-+$/g, "") || `product-${crypto.randomUUID().slice(0, 8)}`;
|
|
8
8
|
}
|
|
9
|
-
function buildWooUrl(base, path, key, secret) {
|
|
9
|
+
function buildWooUrl(base, path, key, secret, page = 1) {
|
|
10
10
|
const url = new URL(path, base.replace(/\/$/, "/"));
|
|
11
11
|
url.searchParams.set("consumer_key", key);
|
|
12
12
|
url.searchParams.set("consumer_secret", secret);
|
|
13
13
|
url.searchParams.set("per_page", "100");
|
|
14
|
+
url.searchParams.set("page", String(page));
|
|
14
15
|
return url.toString();
|
|
15
16
|
}
|
|
17
|
+
function normalizeCurrency(value) {
|
|
18
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
19
|
+
return undefined;
|
|
20
|
+
return value.trim().toUpperCase();
|
|
21
|
+
}
|
|
22
|
+
function settingCurrency(data) {
|
|
23
|
+
if (Array.isArray(data)) {
|
|
24
|
+
for (const entry of data) {
|
|
25
|
+
if (typeof entry !== "object" || entry === null)
|
|
26
|
+
continue;
|
|
27
|
+
const setting = entry;
|
|
28
|
+
if (setting.id === "woocommerce_currency")
|
|
29
|
+
return normalizeCurrency(setting.value);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (typeof data !== "object" || data === null)
|
|
33
|
+
return undefined;
|
|
34
|
+
const object = data;
|
|
35
|
+
return normalizeCurrency(object.woocommerce_currency);
|
|
36
|
+
}
|
|
16
37
|
async function fetchJson(fetchImpl, url) {
|
|
17
38
|
try {
|
|
18
39
|
const response = await fetchImpl(url, { headers: { accept: "application/json" } });
|
|
@@ -22,7 +43,7 @@ async function fetchJson(fetchImpl, url) {
|
|
|
22
43
|
message: `WooCommerce request failed (${response.status}) for ${url}.`,
|
|
23
44
|
});
|
|
24
45
|
}
|
|
25
|
-
return Ok((await response.json()));
|
|
46
|
+
return Ok({ data: (await response.json()), response });
|
|
26
47
|
}
|
|
27
48
|
catch (error) {
|
|
28
49
|
return Err({
|
|
@@ -31,6 +52,10 @@ async function fetchJson(fetchImpl, url) {
|
|
|
31
52
|
});
|
|
32
53
|
}
|
|
33
54
|
}
|
|
55
|
+
function totalPages(response) {
|
|
56
|
+
const value = Number.parseInt(response.headers.get("x-wp-totalpages") ?? "", 10);
|
|
57
|
+
return Number.isFinite(value) && value > 0 ? value : undefined;
|
|
58
|
+
}
|
|
34
59
|
async function loadProducts(options) {
|
|
35
60
|
if (options.products)
|
|
36
61
|
return Ok(options.products);
|
|
@@ -41,7 +66,17 @@ async function loadProducts(options) {
|
|
|
41
66
|
});
|
|
42
67
|
}
|
|
43
68
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
44
|
-
|
|
69
|
+
const products = [];
|
|
70
|
+
for (let page = 1;; page += 1) {
|
|
71
|
+
const response = await fetchJson(fetchImpl, buildWooUrl(options.storeUrl, "/wp-json/wc/v3/products", options.consumerKey, options.consumerSecret, page));
|
|
72
|
+
if (!response.ok)
|
|
73
|
+
return response;
|
|
74
|
+
products.push(...response.value.data);
|
|
75
|
+
if (response.value.data.length === 0 || (totalPages(response.value.response) ?? Number.POSITIVE_INFINITY) <= page) {
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return Ok(products);
|
|
45
80
|
}
|
|
46
81
|
async function loadCustomers(options) {
|
|
47
82
|
if (options.customers)
|
|
@@ -50,7 +85,26 @@ async function loadCustomers(options) {
|
|
|
50
85
|
return Ok([]);
|
|
51
86
|
}
|
|
52
87
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
53
|
-
|
|
88
|
+
const customers = [];
|
|
89
|
+
for (let page = 1;; page += 1) {
|
|
90
|
+
const response = await fetchJson(fetchImpl, buildWooUrl(options.storeUrl, "/wp-json/wc/v3/customers", options.consumerKey, options.consumerSecret, page));
|
|
91
|
+
if (!response.ok)
|
|
92
|
+
return response;
|
|
93
|
+
customers.push(...response.value.data);
|
|
94
|
+
if (response.value.data.length === 0 || (totalPages(response.value.response) ?? Number.POSITIVE_INFINITY) <= page) {
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return Ok(customers);
|
|
99
|
+
}
|
|
100
|
+
async function loadCurrency(options) {
|
|
101
|
+
const configured = normalizeCurrency(options.currency);
|
|
102
|
+
if (configured)
|
|
103
|
+
return configured;
|
|
104
|
+
if (!options.storeUrl || !options.consumerKey || !options.consumerSecret)
|
|
105
|
+
return undefined;
|
|
106
|
+
const response = await fetchJson(options.fetchImpl ?? fetch, buildWooUrl(options.storeUrl, "/wp-json/wc/v3/settings/general", options.consumerKey, options.consumerSecret));
|
|
107
|
+
return response.ok ? settingCurrency(response.value.data) : undefined;
|
|
54
108
|
}
|
|
55
109
|
function filenameFromUrl(url) {
|
|
56
110
|
try {
|
|
@@ -79,6 +133,7 @@ export async function importWooCommerceCatalog(options) {
|
|
|
79
133
|
const customers = await loadCustomers(options);
|
|
80
134
|
if (!customers.ok)
|
|
81
135
|
return customers;
|
|
136
|
+
const currency = await loadCurrency(options);
|
|
82
137
|
const summary = {
|
|
83
138
|
entitiesImported: 0,
|
|
84
139
|
variantsImported: 0,
|
|
@@ -136,6 +191,8 @@ export async function importWooCommerceCatalog(options) {
|
|
|
136
191
|
const variationData = product.variationsData ?? [];
|
|
137
192
|
for (const variant of variationData) {
|
|
138
193
|
try {
|
|
194
|
+
// An unknown store currency falls back to two decimals.
|
|
195
|
+
const price = toMinorUnits(variant.price, currency ?? "");
|
|
139
196
|
const optionValueIds = (variant.attributes ?? [])
|
|
140
197
|
.map((attribute) => optionValueIdByKey.get(`${attribute.name}::${attribute.option ?? ""}`))
|
|
141
198
|
.filter((value) => typeof value === "string");
|
|
@@ -146,7 +203,7 @@ export async function importWooCommerceCatalog(options) {
|
|
|
146
203
|
metadata: {
|
|
147
204
|
source: "woocommerce",
|
|
148
205
|
wooVariationId: variant.id,
|
|
149
|
-
price:
|
|
206
|
+
...(price !== undefined ? { price } : {}),
|
|
150
207
|
},
|
|
151
208
|
});
|
|
152
209
|
summary.variantsImported += 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@porulle/import-woocommerce",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.65.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@porulle/core": "0.
|
|
14
|
+
"@porulle/core": "0.65.0"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@types/node": "^24.5.2",
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Err, Ok, type Result } from "@porulle/core";
|
|
1
|
+
import { Err, Ok, toMinorUnits, type Result } from "@porulle/core";
|
|
2
2
|
|
|
3
3
|
export interface WooImage {
|
|
4
4
|
id: number;
|
|
@@ -126,6 +126,7 @@ export interface WooImportOptions {
|
|
|
126
126
|
consumerSecret?: string;
|
|
127
127
|
products?: WooProduct[];
|
|
128
128
|
customers?: WooCustomer[];
|
|
129
|
+
currency?: string;
|
|
129
130
|
fetchImpl?: typeof fetch;
|
|
130
131
|
mediaFetcher?: (url: string) => Promise<{ data: ArrayBuffer; contentType: string; filename?: string }>;
|
|
131
132
|
entityType?: string;
|
|
@@ -147,15 +148,37 @@ function slugify(value: string): string {
|
|
|
147
148
|
.replace(/^-+|-+$/g, "") || `product-${crypto.randomUUID().slice(0, 8)}`;
|
|
148
149
|
}
|
|
149
150
|
|
|
150
|
-
function buildWooUrl(base: string, path: string, key: string, secret: string): string {
|
|
151
|
+
function buildWooUrl(base: string, path: string, key: string, secret: string, page = 1): string {
|
|
151
152
|
const url = new URL(path, base.replace(/\/$/, "/"));
|
|
152
153
|
url.searchParams.set("consumer_key", key);
|
|
153
154
|
url.searchParams.set("consumer_secret", secret);
|
|
154
155
|
url.searchParams.set("per_page", "100");
|
|
156
|
+
url.searchParams.set("page", String(page));
|
|
155
157
|
return url.toString();
|
|
156
158
|
}
|
|
157
159
|
|
|
158
|
-
|
|
160
|
+
function normalizeCurrency(value: unknown): string | undefined {
|
|
161
|
+
if (typeof value !== "string" || value.trim() === "") return undefined;
|
|
162
|
+
return value.trim().toUpperCase();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function settingCurrency(data: unknown): string | undefined {
|
|
166
|
+
if (Array.isArray(data)) {
|
|
167
|
+
for (const entry of data) {
|
|
168
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
169
|
+
const setting = entry as Record<string, unknown>;
|
|
170
|
+
if (setting.id === "woocommerce_currency") return normalizeCurrency(setting.value);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (typeof data !== "object" || data === null) return undefined;
|
|
174
|
+
const object = data as Record<string, unknown>;
|
|
175
|
+
return normalizeCurrency(object.woocommerce_currency);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function fetchJson<T>(
|
|
179
|
+
fetchImpl: typeof fetch,
|
|
180
|
+
url: string,
|
|
181
|
+
): Promise<Result<{ data: T; response: Response }>> {
|
|
159
182
|
try {
|
|
160
183
|
const response = await fetchImpl(url, { headers: { accept: "application/json" } });
|
|
161
184
|
if (!response.ok) {
|
|
@@ -164,7 +187,7 @@ async function fetchJson<T>(fetchImpl: typeof fetch, url: string): Promise<Resul
|
|
|
164
187
|
message: `WooCommerce request failed (${response.status}) for ${url}.`,
|
|
165
188
|
});
|
|
166
189
|
}
|
|
167
|
-
return Ok((await response.json()) as T);
|
|
190
|
+
return Ok({ data: (await response.json()) as T, response });
|
|
168
191
|
} catch (error) {
|
|
169
192
|
return Err({
|
|
170
193
|
code: "WOO_API_FAILED",
|
|
@@ -173,6 +196,11 @@ async function fetchJson<T>(fetchImpl: typeof fetch, url: string): Promise<Resul
|
|
|
173
196
|
}
|
|
174
197
|
}
|
|
175
198
|
|
|
199
|
+
function totalPages(response: Response): number | undefined {
|
|
200
|
+
const value = Number.parseInt(response.headers.get("x-wp-totalpages") ?? "", 10);
|
|
201
|
+
return Number.isFinite(value) && value > 0 ? value : undefined;
|
|
202
|
+
}
|
|
203
|
+
|
|
176
204
|
async function loadProducts(options: WooImportOptions): Promise<Result<WooProduct[]>> {
|
|
177
205
|
if (options.products) return Ok(options.products);
|
|
178
206
|
if (!options.storeUrl || !options.consumerKey || !options.consumerSecret) {
|
|
@@ -183,10 +211,19 @@ async function loadProducts(options: WooImportOptions): Promise<Result<WooProduc
|
|
|
183
211
|
}
|
|
184
212
|
|
|
185
213
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
214
|
+
const products: WooProduct[] = [];
|
|
215
|
+
for (let page = 1; ; page += 1) {
|
|
216
|
+
const response = await fetchJson<WooProduct[]>(
|
|
217
|
+
fetchImpl,
|
|
218
|
+
buildWooUrl(options.storeUrl, "/wp-json/wc/v3/products", options.consumerKey, options.consumerSecret, page),
|
|
219
|
+
);
|
|
220
|
+
if (!response.ok) return response;
|
|
221
|
+
products.push(...response.value.data);
|
|
222
|
+
if (response.value.data.length === 0 || (totalPages(response.value.response) ?? Number.POSITIVE_INFINITY) <= page) {
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return Ok(products);
|
|
190
227
|
}
|
|
191
228
|
|
|
192
229
|
async function loadCustomers(options: WooImportOptions): Promise<Result<WooCustomer[]>> {
|
|
@@ -196,10 +233,30 @@ async function loadCustomers(options: WooImportOptions): Promise<Result<WooCusto
|
|
|
196
233
|
}
|
|
197
234
|
|
|
198
235
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
236
|
+
const customers: WooCustomer[] = [];
|
|
237
|
+
for (let page = 1; ; page += 1) {
|
|
238
|
+
const response = await fetchJson<WooCustomer[]>(
|
|
239
|
+
fetchImpl,
|
|
240
|
+
buildWooUrl(options.storeUrl, "/wp-json/wc/v3/customers", options.consumerKey, options.consumerSecret, page),
|
|
241
|
+
);
|
|
242
|
+
if (!response.ok) return response;
|
|
243
|
+
customers.push(...response.value.data);
|
|
244
|
+
if (response.value.data.length === 0 || (totalPages(response.value.response) ?? Number.POSITIVE_INFINITY) <= page) {
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return Ok(customers);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function loadCurrency(options: WooImportOptions): Promise<string | undefined> {
|
|
252
|
+
const configured = normalizeCurrency(options.currency);
|
|
253
|
+
if (configured) return configured;
|
|
254
|
+
if (!options.storeUrl || !options.consumerKey || !options.consumerSecret) return undefined;
|
|
255
|
+
const response = await fetchJson<unknown>(
|
|
256
|
+
options.fetchImpl ?? fetch,
|
|
257
|
+
buildWooUrl(options.storeUrl, "/wp-json/wc/v3/settings/general", options.consumerKey, options.consumerSecret),
|
|
202
258
|
);
|
|
259
|
+
return response.ok ? settingCurrency(response.value.data) : undefined;
|
|
203
260
|
}
|
|
204
261
|
|
|
205
262
|
function filenameFromUrl(url: string): string {
|
|
@@ -233,6 +290,7 @@ export async function importWooCommerceCatalog(options: WooImportOptions): Promi
|
|
|
233
290
|
|
|
234
291
|
const customers = await loadCustomers(options);
|
|
235
292
|
if (!customers.ok) return customers;
|
|
293
|
+
const currency = await loadCurrency(options);
|
|
236
294
|
|
|
237
295
|
const summary: WooImportSummary = {
|
|
238
296
|
entitiesImported: 0,
|
|
@@ -298,6 +356,8 @@ export async function importWooCommerceCatalog(options: WooImportOptions): Promi
|
|
|
298
356
|
const variationData = product.variationsData ?? [];
|
|
299
357
|
for (const variant of variationData) {
|
|
300
358
|
try {
|
|
359
|
+
// An unknown store currency falls back to two decimals.
|
|
360
|
+
const price = toMinorUnits(variant.price, currency ?? "");
|
|
301
361
|
const optionValueIds = (variant.attributes ?? [])
|
|
302
362
|
.map((attribute) => optionValueIdByKey.get(`${attribute.name}::${attribute.option ?? ""}`))
|
|
303
363
|
.filter((value): value is string => typeof value === "string");
|
|
@@ -309,7 +369,7 @@ export async function importWooCommerceCatalog(options: WooImportOptions): Promi
|
|
|
309
369
|
metadata: {
|
|
310
370
|
source: "woocommerce",
|
|
311
371
|
wooVariationId: variant.id,
|
|
312
|
-
price:
|
|
372
|
+
...(price !== undefined ? { price } : {}),
|
|
313
373
|
},
|
|
314
374
|
});
|
|
315
375
|
|