@atomic-solutions/woocommerce-api-client 0.4.0 → 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.
@@ -1,4 +1,4 @@
1
- import { S as StoreApiVersion, C as CartHeadersAdapter, J as JwtTokenAdapter } from '../types-BF4_UUqp.mjs';
1
+ import { S as StoreApiVersion, C as CartHeadersAdapter, J as JwtTokenAdapter } from '../types-aYzN4O5x.mjs';
2
2
  import 'axios';
3
3
  import 'zod';
4
4
 
@@ -1,4 +1,4 @@
1
- import { S as StoreApiVersion, C as CartHeadersAdapter, J as JwtTokenAdapter } from '../types-BF4_UUqp.js';
1
+ import { S as StoreApiVersion, C as CartHeadersAdapter, J as JwtTokenAdapter } from '../types-aYzN4O5x.js';
2
2
  import 'axios';
3
3
  import 'zod';
4
4
 
@@ -284,34 +284,34 @@ var WooCommerceDataValidationError = class _WooCommerceDataValidationError exten
284
284
  };
285
285
 
286
286
  // src/http/handleApiResponse.ts
287
- var handleApiResponse = (response, schema, options) => {
287
+ var handleApiData = (data, schema, options, url) => {
288
288
  const validationMode = options?.validationMode ?? "strict";
289
289
  if (validationMode === "warn") {
290
- const result = schema.safeParse(response.data);
290
+ const result = schema.safeParse(data);
291
291
  if (!result.success) {
292
292
  const validationError = new WooCommerceDataValidationError({
293
- message: `WooCommerce API response validation failed for ${response.config.url || "unknown endpoint"}`,
294
- url: response.config.url,
293
+ message: `WooCommerce API response validation failed for ${url || "unknown endpoint"}`,
294
+ url,
295
295
  zodError: result.error,
296
- value: response.data,
296
+ value: data,
297
297
  userMessage: "Received unexpected data from WooCommerce"
298
298
  });
299
299
  options?.onValidationError?.(validationError);
300
300
  options?.errorReporter?.(validationError);
301
301
  console.warn("[woocommerce-utils] Validation warning:", validationError.message);
302
- return response.data;
302
+ return data;
303
303
  }
304
304
  return result.data;
305
305
  }
306
306
  try {
307
- return schema.parse(response.data);
307
+ return schema.parse(data);
308
308
  } catch (error2) {
309
309
  if (error2 && typeof error2 === "object" && "issues" in error2) {
310
310
  const validationError = new WooCommerceDataValidationError({
311
- message: `WooCommerce API response validation failed for ${response.config.url || "unknown endpoint"}`,
312
- url: response.config.url,
311
+ message: `WooCommerce API response validation failed for ${url || "unknown endpoint"}`,
312
+ url,
313
313
  zodError: error2,
314
- value: response.data,
314
+ value: data,
315
315
  userMessage: "Received unexpected data from WooCommerce"
316
316
  });
317
317
  options?.onValidationError?.(validationError);
@@ -321,6 +321,7 @@ var handleApiResponse = (response, schema, options) => {
321
321
  throw error2;
322
322
  }
323
323
  };
324
+ var handleApiResponse = (response, schema, options) => handleApiData(response.data, schema, options, response.config.url);
324
325
 
325
326
  // src/utils/calculatePagination.ts
326
327
  var calculatePagination = (input) => {
@@ -435,6 +436,12 @@ var error = (...args) => {
435
436
  console.error(DEBUG_PREFIX, ...args);
436
437
  };
437
438
 
439
+ // src/http/storeRoutes.ts
440
+ var CART_ROUTE = /\/wc\/store\/(v\d+\/)?(cart|checkout|batch)/;
441
+ var BATCH_ROUTE = /\/wc\/store\/(v\d+\/)?batch/;
442
+ var isCartRoute = (url) => !!url && CART_ROUTE.test(url);
443
+ var isBatchRoute = (url) => !!url && BATCH_ROUTE.test(url);
444
+
438
445
  // src/http/interceptors/request.ts
439
446
  var requiresAuth = (url) => {
440
447
  if (!url) return false;
@@ -448,30 +455,26 @@ var requiresAuth = (url) => {
448
455
  ];
449
456
  return authEndpoints.some((endpoint) => url.includes(endpoint));
450
457
  };
451
- var requiresCartAuth = (url) => {
452
- if (!url) return false;
453
- return /\/wc\/store\/(v\d+\/)?cart/.test(url) || /\/wc\/store\/(v\d+\/)?checkout/.test(url);
454
- };
455
458
  var setupRequestInterceptor = (axiosInstance, config, client) => {
456
459
  axiosInstance.interceptors.request.use(
457
460
  async (requestConfig) => {
458
461
  const url = requestConfig.url;
459
462
  if (config.jwtToken) {
460
463
  const token = await config.jwtToken.get();
461
- if (token && (requiresAuth(url) || requiresCartAuth(url))) {
464
+ if (token && (requiresAuth(url) || isCartRoute(url))) {
462
465
  requestConfig.headers.Authorization = `Bearer ${token}`;
463
466
  if (config.debug) {
464
467
  debug("Injected JWT token for", url);
465
468
  }
466
469
  }
467
470
  }
468
- if (requiresCartAuth(url)) {
471
+ if (isCartRoute(url)) {
469
472
  const headers = await config.cartHeaders.get();
470
473
  const isAuthenticated = !!await config.jwtToken?.get();
471
474
  if (headers.nonce) {
472
475
  requestConfig.headers.Nonce = headers.nonce;
473
476
  }
474
- if (headers.cartToken && !isAuthenticated) {
477
+ if (headers.cartToken && (!isAuthenticated || isBatchRoute(url))) {
475
478
  requestConfig.headers["Cart-Token"] = headers.cartToken;
476
479
  }
477
480
  requestConfig.headers["Cache-Control"] = "no-cache, no-store, must-revalidate";
@@ -500,16 +503,19 @@ var setupRequestInterceptor = (axiosInstance, config, client) => {
500
503
  }
501
504
  );
502
505
  };
503
-
504
- // src/http/interceptors/response.ts
505
- var isCartResponse = (url) => {
506
- if (!url) return false;
507
- return /\/wc\/store\/(v\d+\/)?cart/.test(url) || /\/wc\/store\/(v\d+\/)?checkout/.test(url);
506
+ var NONCE_ERROR_CODES = ["woocommerce_rest_missing_nonce", "woocommerce_rest_invalid_nonce"];
507
+ var isNonceRejectedBatch = (data) => {
508
+ const responses = data?.responses;
509
+ if (!Array.isArray(responses) || responses.length === 0) return false;
510
+ return responses.every((response) => {
511
+ const code = response?.body?.code;
512
+ return typeof code === "string" && NONCE_ERROR_CODES.includes(code);
513
+ });
508
514
  };
509
515
  var setupResponseInterceptor = (axiosInstance, config, client) => {
510
516
  axiosInstance.interceptors.response.use(
511
517
  async (response) => {
512
- if (isCartResponse(response.config.url)) {
518
+ if (isCartRoute(response.config.url)) {
513
519
  const nonce = response.headers["nonce"] || response.headers["Nonce"];
514
520
  const cartToken = response.headers["cart-token"] || response.headers["Cart-Token"];
515
521
  if (nonce || cartToken) {
@@ -529,6 +535,15 @@ var setupResponseInterceptor = (axiosInstance, config, client) => {
529
535
  }
530
536
  }
531
537
  }
538
+ if (isBatchRoute(response.config.url) && isNonceRejectedBatch(response.data)) {
539
+ throw new axios.AxiosError(
540
+ "Batch rejected for a nonce error",
541
+ axios.AxiosError.ERR_BAD_REQUEST,
542
+ response.config,
543
+ response.request,
544
+ { ...response, status: 401, data: { code: "woocommerce_rest_missing_nonce" } }
545
+ );
546
+ }
532
547
  if (config.debug) {
533
548
  debug("Response:", response.config.url, response.status);
534
549
  }
@@ -967,7 +982,7 @@ var cartSchema = zod.z.object({
967
982
  cross_sells: zod.z.array(zod.z.unknown()),
968
983
  fees: zod.z.array(zod.z.unknown())
969
984
  });
970
- zod.z.object({
985
+ var addToCartInputSchema = zod.z.object({
971
986
  id: zod.z.number(),
972
987
  quantity: zod.z.number().int().positive(),
973
988
  variation: zod.z.array(
@@ -977,6 +992,18 @@ zod.z.object({
977
992
  })
978
993
  ).optional()
979
994
  });
995
+ zod.z.object({
996
+ items: zod.z.array(addToCartInputSchema).min(1)
997
+ });
998
+ var batchResponseSchema = zod.z.object({
999
+ responses: zod.z.array(
1000
+ zod.z.object({
1001
+ status: zod.z.number(),
1002
+ body: zod.z.unknown(),
1003
+ headers: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
1004
+ })
1005
+ )
1006
+ });
980
1007
  zod.z.object({
981
1008
  key: zod.z.string(),
982
1009
  quantity: zod.z.number().int().positive()
@@ -1336,6 +1363,19 @@ zod.z.object({
1336
1363
  var ORDER_ATTRIBUTION_NAMESPACE = "woocommerce/order-attribution";
1337
1364
 
1338
1365
  // src/api/cart.ts
1366
+ var BATCH_MAX_REQUESTS = 25;
1367
+ var chunk = (list, size) => Array.from(
1368
+ { length: Math.ceil(list.length / size) },
1369
+ (_, index) => list.slice(index * size, index * size + size)
1370
+ );
1371
+ var isAccepted = (status) => status >= 200 && status < 300;
1372
+ var bodyError = (body) => {
1373
+ const data = body;
1374
+ return {
1375
+ code: typeof data?.code === "string" ? data.code : void 0,
1376
+ message: typeof data?.message === "string" ? data.message : void 0
1377
+ };
1378
+ };
1339
1379
  var createCartAPI = (client, endpoints, options) => ({
1340
1380
  get: async () => {
1341
1381
  const response = await client.get(endpoints.cart);
@@ -1345,6 +1385,48 @@ var createCartAPI = (client, endpoints, options) => ({
1345
1385
  const response = await client.post(endpoints.addItem, input);
1346
1386
  return handleApiResponse(response, cartSchema, options);
1347
1387
  },
1388
+ addItems: async ({ items }) => {
1389
+ const responses = [];
1390
+ let chunkFailed = false;
1391
+ for (const batch of chunk(items, BATCH_MAX_REQUESTS)) {
1392
+ try {
1393
+ const response = await client.post(endpoints.batch, {
1394
+ requests: batch.map((item) => ({
1395
+ method: "POST",
1396
+ path: endpoints.addItem,
1397
+ body: item
1398
+ }))
1399
+ });
1400
+ responses.push(...handleApiResponse(response, batchResponseSchema, options).responses);
1401
+ } catch (error2) {
1402
+ if (responses.length === 0) throw error2;
1403
+ chunkFailed = true;
1404
+ break;
1405
+ }
1406
+ }
1407
+ const results = items.map((item, index) => {
1408
+ const entry = responses[index];
1409
+ const { code, message } = bodyError(entry?.body);
1410
+ return {
1411
+ item,
1412
+ status: entry?.status ?? 500,
1413
+ ...code ? { code } : {},
1414
+ ...message ? { message } : {}
1415
+ };
1416
+ });
1417
+ const lastAccepted = [...responses].reverse().find((entry) => isAccepted(entry.status));
1418
+ const batchCart = lastAccepted ? handleApiData(lastAccepted.body, cartSchema, options, endpoints.batch) : void 0;
1419
+ let cart = chunkFailed ? void 0 : batchCart;
1420
+ if (!cart) {
1421
+ try {
1422
+ cart = handleApiResponse(await client.get(endpoints.cart), cartSchema, options);
1423
+ } catch (error2) {
1424
+ if (!batchCart) throw error2;
1425
+ cart = batchCart;
1426
+ }
1427
+ }
1428
+ return { cart, results };
1429
+ },
1348
1430
  updateItem: async (input) => {
1349
1431
  const response = await client.post(endpoints.updateItem, input);
1350
1432
  return handleApiResponse(response, cartSchema, options);
@@ -1434,6 +1516,8 @@ var createEndpoints = (version = "legacy") => {
1434
1516
  removeCoupon: `${prefix}/cart/remove-coupon`,
1435
1517
  updateCustomer: `${prefix}/cart/update-customer`,
1436
1518
  selectShippingRate: `${prefix}/cart/select-shipping-rate`,
1519
+ // Batch
1520
+ batch: `${prefix}/batch`,
1437
1521
  // Checkout
1438
1522
  checkout: `${prefix}/checkout`,
1439
1523
  // Orders