@atomic-solutions/woocommerce-api-client 0.3.1 → 0.5.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.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import axios, { isAxiosError } from 'axios';
1
+ import axios, { AxiosError, isAxiosError } from 'axios';
2
2
  import { z } from 'zod';
3
3
 
4
4
  // src/client/createClient.ts
@@ -47,6 +47,7 @@ Caused by: ${this.options.cause.stack}`;
47
47
 
48
48
  // src/errors/mapping/mapWooCommerceCode.ts
49
49
  var mapWooCommerceCode = (apiCode) => {
50
+ if (!apiCode) return "unknown_error";
50
51
  const directMatches = {
51
52
  woocommerce_rest_cart_empty: "cart_empty",
52
53
  woocommerce_rest_cart_coupon_error: "invalid_coupon",
@@ -278,34 +279,34 @@ var WooCommerceDataValidationError = class _WooCommerceDataValidationError exten
278
279
  };
279
280
 
280
281
  // src/http/handleApiResponse.ts
281
- var handleApiResponse = (response, schema, options) => {
282
+ var handleApiData = (data, schema, options, url) => {
282
283
  const validationMode = options?.validationMode ?? "strict";
283
284
  if (validationMode === "warn") {
284
- const result = schema.safeParse(response.data);
285
+ const result = schema.safeParse(data);
285
286
  if (!result.success) {
286
287
  const validationError = new WooCommerceDataValidationError({
287
- message: `WooCommerce API response validation failed for ${response.config.url || "unknown endpoint"}`,
288
- url: response.config.url,
288
+ message: `WooCommerce API response validation failed for ${url || "unknown endpoint"}`,
289
+ url,
289
290
  zodError: result.error,
290
- value: response.data,
291
+ value: data,
291
292
  userMessage: "Received unexpected data from WooCommerce"
292
293
  });
293
294
  options?.onValidationError?.(validationError);
294
295
  options?.errorReporter?.(validationError);
295
296
  console.warn("[woocommerce-utils] Validation warning:", validationError.message);
296
- return response.data;
297
+ return data;
297
298
  }
298
299
  return result.data;
299
300
  }
300
301
  try {
301
- return schema.parse(response.data);
302
+ return schema.parse(data);
302
303
  } catch (error2) {
303
304
  if (error2 && typeof error2 === "object" && "issues" in error2) {
304
305
  const validationError = new WooCommerceDataValidationError({
305
- message: `WooCommerce API response validation failed for ${response.config.url || "unknown endpoint"}`,
306
- url: response.config.url,
306
+ message: `WooCommerce API response validation failed for ${url || "unknown endpoint"}`,
307
+ url,
307
308
  zodError: error2,
308
- value: response.data,
309
+ value: data,
309
310
  userMessage: "Received unexpected data from WooCommerce"
310
311
  });
311
312
  options?.onValidationError?.(validationError);
@@ -315,6 +316,7 @@ var handleApiResponse = (response, schema, options) => {
315
316
  throw error2;
316
317
  }
317
318
  };
319
+ var handleApiResponse = (response, schema, options) => handleApiData(response.data, schema, options, response.config.url);
318
320
 
319
321
  // src/utils/calculatePagination.ts
320
322
  var calculatePagination = (input) => {
@@ -429,6 +431,12 @@ var error = (...args) => {
429
431
  console.error(DEBUG_PREFIX, ...args);
430
432
  };
431
433
 
434
+ // src/http/storeRoutes.ts
435
+ var CART_ROUTE = /\/wc\/store\/(v\d+\/)?(cart|checkout|batch)/;
436
+ var BATCH_ROUTE = /\/wc\/store\/(v\d+\/)?batch/;
437
+ var isCartRoute = (url) => !!url && CART_ROUTE.test(url);
438
+ var isBatchRoute = (url) => !!url && BATCH_ROUTE.test(url);
439
+
432
440
  // src/http/interceptors/request.ts
433
441
  var requiresAuth = (url) => {
434
442
  if (!url) return false;
@@ -442,30 +450,26 @@ var requiresAuth = (url) => {
442
450
  ];
443
451
  return authEndpoints.some((endpoint) => url.includes(endpoint));
444
452
  };
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
453
  var setupRequestInterceptor = (axiosInstance, config, client) => {
450
454
  axiosInstance.interceptors.request.use(
451
455
  async (requestConfig) => {
452
456
  const url = requestConfig.url;
453
457
  if (config.jwtToken) {
454
458
  const token = await config.jwtToken.get();
455
- if (token && (requiresAuth(url) || requiresCartAuth(url))) {
459
+ if (token && (requiresAuth(url) || isCartRoute(url))) {
456
460
  requestConfig.headers.Authorization = `Bearer ${token}`;
457
461
  if (config.debug) {
458
462
  debug("Injected JWT token for", url);
459
463
  }
460
464
  }
461
465
  }
462
- if (requiresCartAuth(url)) {
466
+ if (isCartRoute(url)) {
463
467
  const headers = await config.cartHeaders.get();
464
468
  const isAuthenticated = !!await config.jwtToken?.get();
465
469
  if (headers.nonce) {
466
470
  requestConfig.headers.Nonce = headers.nonce;
467
471
  }
468
- if (headers.cartToken && !isAuthenticated) {
472
+ if (headers.cartToken && (!isAuthenticated || isBatchRoute(url))) {
469
473
  requestConfig.headers["Cart-Token"] = headers.cartToken;
470
474
  }
471
475
  requestConfig.headers["Cache-Control"] = "no-cache, no-store, must-revalidate";
@@ -494,16 +498,19 @@ var setupRequestInterceptor = (axiosInstance, config, client) => {
494
498
  }
495
499
  );
496
500
  };
497
-
498
- // src/http/interceptors/response.ts
499
- var isCartResponse = (url) => {
500
- if (!url) return false;
501
- return /\/wc\/store\/(v\d+\/)?cart/.test(url) || /\/wc\/store\/(v\d+\/)?checkout/.test(url);
501
+ var NONCE_ERROR_CODES = ["woocommerce_rest_missing_nonce", "woocommerce_rest_invalid_nonce"];
502
+ var isNonceRejectedBatch = (data) => {
503
+ const responses = data?.responses;
504
+ if (!Array.isArray(responses) || responses.length === 0) return false;
505
+ return responses.every((response) => {
506
+ const code = response?.body?.code;
507
+ return typeof code === "string" && NONCE_ERROR_CODES.includes(code);
508
+ });
502
509
  };
503
510
  var setupResponseInterceptor = (axiosInstance, config, client) => {
504
511
  axiosInstance.interceptors.response.use(
505
512
  async (response) => {
506
- if (isCartResponse(response.config.url)) {
513
+ if (isCartRoute(response.config.url)) {
507
514
  const nonce = response.headers["nonce"] || response.headers["Nonce"];
508
515
  const cartToken = response.headers["cart-token"] || response.headers["Cart-Token"];
509
516
  if (nonce || cartToken) {
@@ -523,6 +530,15 @@ var setupResponseInterceptor = (axiosInstance, config, client) => {
523
530
  }
524
531
  }
525
532
  }
533
+ if (isBatchRoute(response.config.url) && isNonceRejectedBatch(response.data)) {
534
+ throw new AxiosError(
535
+ "Batch rejected for a nonce error",
536
+ AxiosError.ERR_BAD_REQUEST,
537
+ response.config,
538
+ response.request,
539
+ { ...response, status: 401, data: { code: "woocommerce_rest_missing_nonce" } }
540
+ );
541
+ }
526
542
  if (config.debug) {
527
543
  debug("Response:", response.config.url, response.status);
528
544
  }
@@ -1020,6 +1036,18 @@ var addToCartInputSchema = z.object({
1020
1036
  })
1021
1037
  ).optional()
1022
1038
  });
1039
+ var addItemsInputSchema = z.object({
1040
+ items: z.array(addToCartInputSchema).min(1)
1041
+ });
1042
+ var batchResponseSchema = z.object({
1043
+ responses: z.array(
1044
+ z.object({
1045
+ status: z.number(),
1046
+ body: z.unknown(),
1047
+ headers: z.record(z.string(), z.unknown()).optional()
1048
+ })
1049
+ )
1050
+ });
1023
1051
  var updateCartItemInputSchema = z.object({
1024
1052
  key: z.string(),
1025
1053
  quantity: z.number().int().positive()
@@ -1363,6 +1391,8 @@ var storeProductsSearchParamsSchema = z.object({
1363
1391
  search: z.string().optional(),
1364
1392
  /** Exact slug match */
1365
1393
  slug: z.string().optional(),
1394
+ /** Exact SKU match */
1395
+ sku: z.string().optional(),
1366
1396
  // Date filtering
1367
1397
  /** Filter products published after this date */
1368
1398
  after: z.string().datetime().optional(),
@@ -1377,6 +1407,8 @@ var storeProductsSearchParamsSchema = z.object({
1377
1407
  include: z.array(z.number()).optional(),
1378
1408
  /** Exclude specific product IDs */
1379
1409
  exclude: z.array(z.number()).optional(),
1410
+ /** Return products related to this product ID */
1411
+ related: z.number().optional(),
1380
1412
  // Parent/Child
1381
1413
  /** Filter by parent product IDs */
1382
1414
  parent: z.array(z.number()).optional(),
@@ -1476,6 +1508,19 @@ z.object({
1476
1508
  var ORDER_ATTRIBUTION_NAMESPACE = "woocommerce/order-attribution";
1477
1509
 
1478
1510
  // src/api/cart.ts
1511
+ var BATCH_MAX_REQUESTS = 25;
1512
+ var chunk = (list, size) => Array.from(
1513
+ { length: Math.ceil(list.length / size) },
1514
+ (_, index) => list.slice(index * size, index * size + size)
1515
+ );
1516
+ var isAccepted = (status) => status >= 200 && status < 300;
1517
+ var bodyError = (body) => {
1518
+ const data = body;
1519
+ return {
1520
+ code: typeof data?.code === "string" ? data.code : void 0,
1521
+ message: typeof data?.message === "string" ? data.message : void 0
1522
+ };
1523
+ };
1479
1524
  var createCartAPI = (client, endpoints, options) => ({
1480
1525
  get: async () => {
1481
1526
  const response = await client.get(endpoints.cart);
@@ -1485,6 +1530,48 @@ var createCartAPI = (client, endpoints, options) => ({
1485
1530
  const response = await client.post(endpoints.addItem, input);
1486
1531
  return handleApiResponse(response, cartSchema, options);
1487
1532
  },
1533
+ addItems: async ({ items }) => {
1534
+ const responses = [];
1535
+ let chunkFailed = false;
1536
+ for (const batch of chunk(items, BATCH_MAX_REQUESTS)) {
1537
+ try {
1538
+ const response = await client.post(endpoints.batch, {
1539
+ requests: batch.map((item) => ({
1540
+ method: "POST",
1541
+ path: endpoints.addItem,
1542
+ body: item
1543
+ }))
1544
+ });
1545
+ responses.push(...handleApiResponse(response, batchResponseSchema, options).responses);
1546
+ } catch (error2) {
1547
+ if (responses.length === 0) throw error2;
1548
+ chunkFailed = true;
1549
+ break;
1550
+ }
1551
+ }
1552
+ const results = items.map((item, index) => {
1553
+ const entry = responses[index];
1554
+ const { code, message } = bodyError(entry?.body);
1555
+ return {
1556
+ item,
1557
+ status: entry?.status ?? 500,
1558
+ ...code ? { code } : {},
1559
+ ...message ? { message } : {}
1560
+ };
1561
+ });
1562
+ const lastAccepted = [...responses].reverse().find((entry) => isAccepted(entry.status));
1563
+ const batchCart = lastAccepted ? handleApiData(lastAccepted.body, cartSchema, options, endpoints.batch) : void 0;
1564
+ let cart = chunkFailed ? void 0 : batchCart;
1565
+ if (!cart) {
1566
+ try {
1567
+ cart = handleApiResponse(await client.get(endpoints.cart), cartSchema, options);
1568
+ } catch (error2) {
1569
+ if (!batchCart) throw error2;
1570
+ cart = batchCart;
1571
+ }
1572
+ }
1573
+ return { cart, results };
1574
+ },
1488
1575
  updateItem: async (input) => {
1489
1576
  const response = await client.post(endpoints.updateItem, input);
1490
1577
  return handleApiResponse(response, cartSchema, options);
@@ -1574,6 +1661,8 @@ var createEndpoints = (version = "legacy") => {
1574
1661
  removeCoupon: `${prefix}/cart/remove-coupon`,
1575
1662
  updateCustomer: `${prefix}/cart/update-customer`,
1576
1663
  selectShippingRate: `${prefix}/cart/select-shipping-rate`,
1664
+ // Batch
1665
+ batch: `${prefix}/batch`,
1577
1666
  // Checkout
1578
1667
  checkout: `${prefix}/checkout`,
1579
1668
  // Orders
@@ -2160,6 +2249,6 @@ var schemas = {
2160
2249
  }
2161
2250
  };
2162
2251
 
2163
- export { WooCommerceApiError, WooCommerceDataValidationError, addToCartInputSchema, addressSchema, categoryFilterSchema as adminCategoryFilterSchema, couponFilterSchema as adminCouponFilterSchema, couponSchema as adminCouponSchema, orderFilterSchema as adminOrderFilterSchema, orderSchema as adminOrderSchema, productCategorySchema2 as adminProductCategorySchema, productFilterSchema as adminProductFilterSchema, productSchema2 as adminProductSchema, billingAddressSchemaMandatory as billingAddressSchema, cartItemImageSchema, cartSchema, checkoutInputSchema, checkoutSchema, couponInputSchema, createCategorySchema, createClient, createCouponSchema, createCustomerSchema, createPaginatedSchema, customerFilterSchema, customerSchema, getAppEnumRaw, getAppEnumZod, makeAddressFormSchema, makeBillingAddressFormSchema, paginationMetaSchema, paginationParamsSchema, paymentMethodFilterSchema, paymentMethodSchema, productCategorySchema, productImageSchema, productSchema, removeCartItemInputSchema, schemas, storeProductsSearchParamsSchema as searchParamsSchema, selectShippingRateInputSchema, storeApiOrderSchema, updateCartItemInputSchema, updateCustomerInputSchema, updateCustomerSchema, wcBaseParamsSchema, wcErrorResponseSchema, wcPaginationMetaSchema };
2252
+ export { WooCommerceApiError, WooCommerceDataValidationError, addItemsInputSchema, addToCartInputSchema, addressSchema, categoryFilterSchema as adminCategoryFilterSchema, couponFilterSchema as adminCouponFilterSchema, couponSchema as adminCouponSchema, orderFilterSchema as adminOrderFilterSchema, orderSchema as adminOrderSchema, productCategorySchema2 as adminProductCategorySchema, productFilterSchema as adminProductFilterSchema, productSchema2 as adminProductSchema, batchResponseSchema, billingAddressSchemaMandatory as billingAddressSchema, cartItemImageSchema, cartSchema, checkoutInputSchema, checkoutSchema, couponInputSchema, createCategorySchema, createClient, createCouponSchema, createCustomerSchema, createPaginatedSchema, customerFilterSchema, customerSchema, getAppEnumRaw, getAppEnumZod, makeAddressFormSchema, makeBillingAddressFormSchema, mapWooCommerceCode, paginationMetaSchema, paginationParamsSchema, paymentMethodFilterSchema, paymentMethodSchema, productCategorySchema, productImageSchema, productSchema, removeCartItemInputSchema, schemas, storeProductsSearchParamsSchema as searchParamsSchema, selectShippingRateInputSchema, storeApiOrderSchema, updateCartItemInputSchema, updateCustomerInputSchema, updateCustomerSchema, wcBaseParamsSchema, wcErrorResponseSchema, wcPaginationMetaSchema };
2164
2253
  //# sourceMappingURL=index.mjs.map
2165
2254
  //# sourceMappingURL=index.mjs.map