@atomic-solutions/woocommerce-api-client 0.4.0 → 0.5.1
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/client/index.d.mts +3 -2
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.js +156 -50
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +157 -51
- package/dist/client/index.mjs.map +1 -1
- package/dist/http/index.d.mts +3 -2
- package/dist/http/index.d.ts +3 -2
- package/dist/http/index.js +88 -48
- package/dist/http/index.js.map +1 -1
- package/dist/http/index.mjs +89 -50
- package/dist/http/index.mjs.map +1 -1
- package/dist/index.d.mts +23 -13
- package/dist/index.d.ts +23 -13
- package/dist/index.js +163 -49
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +162 -51
- package/dist/index.mjs.map +1 -1
- package/dist/testing/index.d.mts +2 -1
- package/dist/testing/index.d.ts +2 -1
- package/dist/testing/index.js +156 -50
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/index.mjs +157 -51
- package/dist/testing/index.mjs.map +1 -1
- package/dist/{types-BF4_UUqp.d.mts → types-9I6oS_83.d.mts} +105 -23
- package/dist/{types-BF4_UUqp.d.ts → types-9I6oS_83.d.ts} +105 -23
- package/dist/utils/index.d.mts +3 -2
- package/dist/utils/index.d.ts +3 -2
- package/dist/utils/index.js +11 -10
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +11 -10
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/client/index.mjs
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import axios, { isAxiosError } from 'axios';
|
|
1
|
+
import axios, { AxiosError, isCancel, isAxiosError } from 'axios';
|
|
2
|
+
import { BaseError as BaseError$1 } from '@atomic-solutions/errors';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
var __typeError = (msg) => {
|
|
6
|
+
throw TypeError(msg);
|
|
7
|
+
};
|
|
8
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
9
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
10
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
11
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
5
12
|
|
|
6
13
|
// src/errors/classes/BaseError.ts
|
|
7
14
|
var BaseError = class extends Error {
|
|
@@ -130,8 +137,16 @@ var WOOCOMMERCE_ERROR_METADATA = {
|
|
|
130
137
|
}
|
|
131
138
|
};
|
|
132
139
|
|
|
140
|
+
// src/errors/classes/http-error.ts
|
|
141
|
+
var redactPath = (url) => url.replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]+/i, "").replace(/[?#].*$/, "").split("/").map((segment) => /^(\d+|[0-9a-f]{16,}|[0-9a-f-]{36})$/i.test(segment) ? ":id" : segment).join("/");
|
|
142
|
+
var classifyHttpStatus = (status) => ({
|
|
143
|
+
domain: status === void 0 ? "network" : status === 401 || status === 403 ? "auth" : "integration",
|
|
144
|
+
severity: status !== void 0 && status < 500 ? "warning" : "error"
|
|
145
|
+
});
|
|
146
|
+
|
|
133
147
|
// src/errors/classes/WooCommerceApiError.ts
|
|
134
|
-
var
|
|
148
|
+
var _options;
|
|
149
|
+
var WooCommerceApiError = class extends BaseError$1 {
|
|
135
150
|
constructor(options) {
|
|
136
151
|
const metadata = WOOCOMMERCE_ERROR_METADATA[options.code] ?? {
|
|
137
152
|
operation: "woocommerce_api_request",
|
|
@@ -143,7 +158,30 @@ var WooCommerceApiError = class extends BaseError {
|
|
|
143
158
|
userMessage: options.userMessage || metadata.userMessage,
|
|
144
159
|
retryable: options.retryable ?? metadata.retryable
|
|
145
160
|
};
|
|
146
|
-
|
|
161
|
+
const status = enrichedOptions.statusCode;
|
|
162
|
+
const { domain, severity } = classifyHttpStatus(status);
|
|
163
|
+
super({
|
|
164
|
+
domain,
|
|
165
|
+
severity,
|
|
166
|
+
operation: enrichedOptions.operation,
|
|
167
|
+
message: enrichedOptions.message,
|
|
168
|
+
code: enrichedOptions.code,
|
|
169
|
+
retryable: enrichedOptions.retryable,
|
|
170
|
+
userMessage: enrichedOptions.userMessage,
|
|
171
|
+
cause: enrichedOptions.cause,
|
|
172
|
+
fingerprint: [domain, enrichedOptions.code, String(status ?? "network")],
|
|
173
|
+
extras: {
|
|
174
|
+
originalCode: enrichedOptions.originalCode,
|
|
175
|
+
statusCode: status,
|
|
176
|
+
path: redactPath(enrichedOptions.url),
|
|
177
|
+
method: enrichedOptions.method
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
__privateAdd(this, _options);
|
|
181
|
+
__privateSet(this, _options, enrichedOptions);
|
|
182
|
+
}
|
|
183
|
+
get options() {
|
|
184
|
+
return __privateGet(this, _options);
|
|
147
185
|
}
|
|
148
186
|
// Convenient getters for frequently accessed fields (no duplication)
|
|
149
187
|
get wooCode() {
|
|
@@ -170,24 +208,8 @@ var WooCommerceApiError = class extends BaseError {
|
|
|
170
208
|
get data() {
|
|
171
209
|
return this.options.data;
|
|
172
210
|
}
|
|
173
|
-
/**
|
|
174
|
-
* Get reportable data for error tracking/logging
|
|
175
|
-
* Includes HTTP context and WooCommerce-specific fields
|
|
176
|
-
*/
|
|
177
|
-
getReportableData() {
|
|
178
|
-
return {
|
|
179
|
-
code: this.options.code,
|
|
180
|
-
operation: this.options.operation,
|
|
181
|
-
userMessage: this.options.userMessage,
|
|
182
|
-
retryable: this.options.retryable,
|
|
183
|
-
statusCode: this.options.statusCode,
|
|
184
|
-
url: this.options.url,
|
|
185
|
-
method: this.options.method,
|
|
186
|
-
wooCode: this.options.code,
|
|
187
|
-
originalCode: this.options.originalCode
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
211
|
};
|
|
212
|
+
_options = new WeakMap();
|
|
191
213
|
|
|
192
214
|
// src/errors/classes/WooCommerceDataValidationError.ts
|
|
193
215
|
var WooCommerceDataValidationError = class _WooCommerceDataValidationError extends BaseError {
|
|
@@ -278,34 +300,34 @@ var WooCommerceDataValidationError = class _WooCommerceDataValidationError exten
|
|
|
278
300
|
};
|
|
279
301
|
|
|
280
302
|
// src/http/handleApiResponse.ts
|
|
281
|
-
var
|
|
303
|
+
var handleApiData = (data, schema, options, url) => {
|
|
282
304
|
const validationMode = options?.validationMode ?? "strict";
|
|
283
305
|
if (validationMode === "warn") {
|
|
284
|
-
const result = schema.safeParse(
|
|
306
|
+
const result = schema.safeParse(data);
|
|
285
307
|
if (!result.success) {
|
|
286
308
|
const validationError = new WooCommerceDataValidationError({
|
|
287
|
-
message: `WooCommerce API response validation failed for ${
|
|
288
|
-
url
|
|
309
|
+
message: `WooCommerce API response validation failed for ${url || "unknown endpoint"}`,
|
|
310
|
+
url,
|
|
289
311
|
zodError: result.error,
|
|
290
|
-
value:
|
|
312
|
+
value: data,
|
|
291
313
|
userMessage: "Received unexpected data from WooCommerce"
|
|
292
314
|
});
|
|
293
315
|
options?.onValidationError?.(validationError);
|
|
294
316
|
options?.errorReporter?.(validationError);
|
|
295
317
|
console.warn("[woocommerce-utils] Validation warning:", validationError.message);
|
|
296
|
-
return
|
|
318
|
+
return data;
|
|
297
319
|
}
|
|
298
320
|
return result.data;
|
|
299
321
|
}
|
|
300
322
|
try {
|
|
301
|
-
return schema.parse(
|
|
323
|
+
return schema.parse(data);
|
|
302
324
|
} catch (error2) {
|
|
303
325
|
if (error2 && typeof error2 === "object" && "issues" in error2) {
|
|
304
326
|
const validationError = new WooCommerceDataValidationError({
|
|
305
|
-
message: `WooCommerce API response validation failed for ${
|
|
306
|
-
url
|
|
327
|
+
message: `WooCommerce API response validation failed for ${url || "unknown endpoint"}`,
|
|
328
|
+
url,
|
|
307
329
|
zodError: error2,
|
|
308
|
-
value:
|
|
330
|
+
value: data,
|
|
309
331
|
userMessage: "Received unexpected data from WooCommerce"
|
|
310
332
|
});
|
|
311
333
|
options?.onValidationError?.(validationError);
|
|
@@ -315,6 +337,7 @@ var handleApiResponse = (response, schema, options) => {
|
|
|
315
337
|
throw error2;
|
|
316
338
|
}
|
|
317
339
|
};
|
|
340
|
+
var handleApiResponse = (response, schema, options) => handleApiData(response.data, schema, options, response.config.url);
|
|
318
341
|
|
|
319
342
|
// src/utils/calculatePagination.ts
|
|
320
343
|
var calculatePagination = (input) => {
|
|
@@ -429,6 +452,12 @@ var error = (...args) => {
|
|
|
429
452
|
console.error(DEBUG_PREFIX, ...args);
|
|
430
453
|
};
|
|
431
454
|
|
|
455
|
+
// src/http/storeRoutes.ts
|
|
456
|
+
var CART_ROUTE = /\/wc\/store\/(v\d+\/)?(cart|checkout|batch)/;
|
|
457
|
+
var BATCH_ROUTE = /\/wc\/store\/(v\d+\/)?batch/;
|
|
458
|
+
var isCartRoute = (url) => !!url && CART_ROUTE.test(url);
|
|
459
|
+
var isBatchRoute = (url) => !!url && BATCH_ROUTE.test(url);
|
|
460
|
+
|
|
432
461
|
// src/http/interceptors/request.ts
|
|
433
462
|
var requiresAuth = (url) => {
|
|
434
463
|
if (!url) return false;
|
|
@@ -442,30 +471,26 @@ var requiresAuth = (url) => {
|
|
|
442
471
|
];
|
|
443
472
|
return authEndpoints.some((endpoint) => url.includes(endpoint));
|
|
444
473
|
};
|
|
445
|
-
var requiresCartAuth = (url) => {
|
|
446
|
-
if (!url) return false;
|
|
447
|
-
return /\/wc\/store\/(v\d+\/)?cart/.test(url) || /\/wc\/store\/(v\d+\/)?checkout/.test(url);
|
|
448
|
-
};
|
|
449
474
|
var setupRequestInterceptor = (axiosInstance, config, client) => {
|
|
450
475
|
axiosInstance.interceptors.request.use(
|
|
451
476
|
async (requestConfig) => {
|
|
452
477
|
const url = requestConfig.url;
|
|
453
478
|
if (config.jwtToken) {
|
|
454
479
|
const token = await config.jwtToken.get();
|
|
455
|
-
if (token && (requiresAuth(url) ||
|
|
480
|
+
if (token && (requiresAuth(url) || isCartRoute(url))) {
|
|
456
481
|
requestConfig.headers.Authorization = `Bearer ${token}`;
|
|
457
482
|
if (config.debug) {
|
|
458
483
|
debug("Injected JWT token for", url);
|
|
459
484
|
}
|
|
460
485
|
}
|
|
461
486
|
}
|
|
462
|
-
if (
|
|
487
|
+
if (isCartRoute(url)) {
|
|
463
488
|
const headers = await config.cartHeaders.get();
|
|
464
489
|
const isAuthenticated = !!await config.jwtToken?.get();
|
|
465
490
|
if (headers.nonce) {
|
|
466
491
|
requestConfig.headers.Nonce = headers.nonce;
|
|
467
492
|
}
|
|
468
|
-
if (headers.cartToken && !isAuthenticated) {
|
|
493
|
+
if (headers.cartToken && (!isAuthenticated || isBatchRoute(url))) {
|
|
469
494
|
requestConfig.headers["Cart-Token"] = headers.cartToken;
|
|
470
495
|
}
|
|
471
496
|
requestConfig.headers["Cache-Control"] = "no-cache, no-store, must-revalidate";
|
|
@@ -494,16 +519,19 @@ var setupRequestInterceptor = (axiosInstance, config, client) => {
|
|
|
494
519
|
}
|
|
495
520
|
);
|
|
496
521
|
};
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
if (!
|
|
501
|
-
return
|
|
522
|
+
var NONCE_ERROR_CODES = ["woocommerce_rest_missing_nonce", "woocommerce_rest_invalid_nonce"];
|
|
523
|
+
var isNonceRejectedBatch = (data) => {
|
|
524
|
+
const responses = data?.responses;
|
|
525
|
+
if (!Array.isArray(responses) || responses.length === 0) return false;
|
|
526
|
+
return responses.every((response) => {
|
|
527
|
+
const code = response?.body?.code;
|
|
528
|
+
return typeof code === "string" && NONCE_ERROR_CODES.includes(code);
|
|
529
|
+
});
|
|
502
530
|
};
|
|
503
531
|
var setupResponseInterceptor = (axiosInstance, config, client) => {
|
|
504
532
|
axiosInstance.interceptors.response.use(
|
|
505
533
|
async (response) => {
|
|
506
|
-
if (
|
|
534
|
+
if (isCartRoute(response.config.url)) {
|
|
507
535
|
const nonce = response.headers["nonce"] || response.headers["Nonce"];
|
|
508
536
|
const cartToken = response.headers["cart-token"] || response.headers["Cart-Token"];
|
|
509
537
|
if (nonce || cartToken) {
|
|
@@ -523,6 +551,15 @@ var setupResponseInterceptor = (axiosInstance, config, client) => {
|
|
|
523
551
|
}
|
|
524
552
|
}
|
|
525
553
|
}
|
|
554
|
+
if (isBatchRoute(response.config.url) && isNonceRejectedBatch(response.data)) {
|
|
555
|
+
throw new AxiosError(
|
|
556
|
+
"Batch rejected for a nonce error",
|
|
557
|
+
AxiosError.ERR_BAD_REQUEST,
|
|
558
|
+
response.config,
|
|
559
|
+
response.request,
|
|
560
|
+
{ ...response, status: 401, data: { code: "woocommerce_rest_missing_nonce" } }
|
|
561
|
+
);
|
|
562
|
+
}
|
|
526
563
|
if (config.debug) {
|
|
527
564
|
debug("Response:", response.config.url, response.status);
|
|
528
565
|
}
|
|
@@ -565,12 +602,15 @@ var setupErrorInterceptor = (axiosInstance, config, client) => {
|
|
|
565
602
|
axiosInstance.interceptors.response.use(
|
|
566
603
|
(response) => response,
|
|
567
604
|
async (axiosError) => {
|
|
568
|
-
|
|
605
|
+
if (isCancel(axiosError)) {
|
|
606
|
+
return Promise.reject(axiosError);
|
|
607
|
+
}
|
|
608
|
+
const status = axiosError.response?.status;
|
|
569
609
|
const errorData = axiosError.response?.data;
|
|
610
|
+
const isNetworkError = isAxiosError(axiosError) && !axiosError.response;
|
|
570
611
|
const apiCode = errorData?.code || "unknown_error";
|
|
571
612
|
const wpMessage = errorData?.message || axiosError.message || "Unknown error";
|
|
572
|
-
const errorCode = mapWooCommerceCode(apiCode);
|
|
573
|
-
const errorMetadata = WOOCOMMERCE_ERROR_METADATA[errorCode];
|
|
613
|
+
const errorCode = isNetworkError ? "network_error" : mapWooCommerceCode(apiCode);
|
|
574
614
|
const wcError = new WooCommerceApiError({
|
|
575
615
|
message: wpMessage,
|
|
576
616
|
statusCode: status,
|
|
@@ -578,9 +618,6 @@ var setupErrorInterceptor = (axiosInstance, config, client) => {
|
|
|
578
618
|
// Transformed app-level code
|
|
579
619
|
originalCode: apiCode,
|
|
580
620
|
// Original WooCommerce API code
|
|
581
|
-
retryable: errorMetadata.retryable,
|
|
582
|
-
userMessage: errorMetadata.userMessage,
|
|
583
|
-
operation: errorMetadata.operation,
|
|
584
621
|
url: axiosError.config?.url || "",
|
|
585
622
|
method: axiosError.config?.method?.toUpperCase() || "GET",
|
|
586
623
|
requestBody: axiosError.config?.data,
|
|
@@ -961,7 +998,7 @@ var cartSchema = z.object({
|
|
|
961
998
|
cross_sells: z.array(z.unknown()),
|
|
962
999
|
fees: z.array(z.unknown())
|
|
963
1000
|
});
|
|
964
|
-
z.object({
|
|
1001
|
+
var addToCartInputSchema = z.object({
|
|
965
1002
|
id: z.number(),
|
|
966
1003
|
quantity: z.number().int().positive(),
|
|
967
1004
|
variation: z.array(
|
|
@@ -971,6 +1008,18 @@ z.object({
|
|
|
971
1008
|
})
|
|
972
1009
|
).optional()
|
|
973
1010
|
});
|
|
1011
|
+
z.object({
|
|
1012
|
+
items: z.array(addToCartInputSchema).min(1)
|
|
1013
|
+
});
|
|
1014
|
+
var batchResponseSchema = z.object({
|
|
1015
|
+
responses: z.array(
|
|
1016
|
+
z.object({
|
|
1017
|
+
status: z.number(),
|
|
1018
|
+
body: z.unknown(),
|
|
1019
|
+
headers: z.record(z.string(), z.unknown()).optional()
|
|
1020
|
+
})
|
|
1021
|
+
)
|
|
1022
|
+
});
|
|
974
1023
|
z.object({
|
|
975
1024
|
key: z.string(),
|
|
976
1025
|
quantity: z.number().int().positive()
|
|
@@ -1330,6 +1379,19 @@ z.object({
|
|
|
1330
1379
|
var ORDER_ATTRIBUTION_NAMESPACE = "woocommerce/order-attribution";
|
|
1331
1380
|
|
|
1332
1381
|
// src/api/cart.ts
|
|
1382
|
+
var BATCH_MAX_REQUESTS = 25;
|
|
1383
|
+
var chunk = (list, size) => Array.from(
|
|
1384
|
+
{ length: Math.ceil(list.length / size) },
|
|
1385
|
+
(_, index) => list.slice(index * size, index * size + size)
|
|
1386
|
+
);
|
|
1387
|
+
var isAccepted = (status) => status >= 200 && status < 300;
|
|
1388
|
+
var bodyError = (body) => {
|
|
1389
|
+
const data = body;
|
|
1390
|
+
return {
|
|
1391
|
+
code: typeof data?.code === "string" ? data.code : void 0,
|
|
1392
|
+
message: typeof data?.message === "string" ? data.message : void 0
|
|
1393
|
+
};
|
|
1394
|
+
};
|
|
1333
1395
|
var createCartAPI = (client, endpoints, options) => ({
|
|
1334
1396
|
get: async () => {
|
|
1335
1397
|
const response = await client.get(endpoints.cart);
|
|
@@ -1339,6 +1401,48 @@ var createCartAPI = (client, endpoints, options) => ({
|
|
|
1339
1401
|
const response = await client.post(endpoints.addItem, input);
|
|
1340
1402
|
return handleApiResponse(response, cartSchema, options);
|
|
1341
1403
|
},
|
|
1404
|
+
addItems: async ({ items }) => {
|
|
1405
|
+
const responses = [];
|
|
1406
|
+
let chunkFailed = false;
|
|
1407
|
+
for (const batch of chunk(items, BATCH_MAX_REQUESTS)) {
|
|
1408
|
+
try {
|
|
1409
|
+
const response = await client.post(endpoints.batch, {
|
|
1410
|
+
requests: batch.map((item) => ({
|
|
1411
|
+
method: "POST",
|
|
1412
|
+
path: endpoints.addItem,
|
|
1413
|
+
body: item
|
|
1414
|
+
}))
|
|
1415
|
+
});
|
|
1416
|
+
responses.push(...handleApiResponse(response, batchResponseSchema, options).responses);
|
|
1417
|
+
} catch (error2) {
|
|
1418
|
+
if (responses.length === 0) throw error2;
|
|
1419
|
+
chunkFailed = true;
|
|
1420
|
+
break;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
const results = items.map((item, index) => {
|
|
1424
|
+
const entry = responses[index];
|
|
1425
|
+
const { code, message } = bodyError(entry?.body);
|
|
1426
|
+
return {
|
|
1427
|
+
item,
|
|
1428
|
+
status: entry?.status ?? 500,
|
|
1429
|
+
...code ? { code } : {},
|
|
1430
|
+
...message ? { message } : {}
|
|
1431
|
+
};
|
|
1432
|
+
});
|
|
1433
|
+
const lastAccepted = [...responses].reverse().find((entry) => isAccepted(entry.status));
|
|
1434
|
+
const batchCart = lastAccepted ? handleApiData(lastAccepted.body, cartSchema, options, endpoints.batch) : void 0;
|
|
1435
|
+
let cart = chunkFailed ? void 0 : batchCart;
|
|
1436
|
+
if (!cart) {
|
|
1437
|
+
try {
|
|
1438
|
+
cart = handleApiResponse(await client.get(endpoints.cart), cartSchema, options);
|
|
1439
|
+
} catch (error2) {
|
|
1440
|
+
if (!batchCart) throw error2;
|
|
1441
|
+
cart = batchCart;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
return { cart, results };
|
|
1445
|
+
},
|
|
1342
1446
|
updateItem: async (input) => {
|
|
1343
1447
|
const response = await client.post(endpoints.updateItem, input);
|
|
1344
1448
|
return handleApiResponse(response, cartSchema, options);
|
|
@@ -1428,6 +1532,8 @@ var createEndpoints = (version = "legacy") => {
|
|
|
1428
1532
|
removeCoupon: `${prefix}/cart/remove-coupon`,
|
|
1429
1533
|
updateCustomer: `${prefix}/cart/update-customer`,
|
|
1430
1534
|
selectShippingRate: `${prefix}/cart/select-shipping-rate`,
|
|
1535
|
+
// Batch
|
|
1536
|
+
batch: `${prefix}/batch`,
|
|
1431
1537
|
// Checkout
|
|
1432
1538
|
checkout: `${prefix}/checkout`,
|
|
1433
1539
|
// Orders
|