@kizlo/woocommerce 0.4.0 → 0.6.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.js CHANGED
@@ -31,10 +31,10 @@ function encodeSecret(secret) {
31
31
  return new TextEncoder().encode(secret);
32
32
  }
33
33
  function getCartHeaders(options) {
34
- const { connInfo, userId, token } = options;
34
+ const { connInfo, email, token } = options;
35
35
  const headers = {};
36
36
  if (token) headers["X-Kizlo-Guest-Token"] = token;
37
- if (userId) headers["X-Kizlo-User-Id"] = String(userId);
37
+ if (email) headers["X-Kizlo-User-Email"] = email;
38
38
  if (connInfo?.city) headers["X-Kizlo-Geo-City"] = connInfo.city;
39
39
  if (connInfo?.state) headers["X-Kizlo-Geo-State"] = connInfo.state;
40
40
  if (connInfo?.country) headers["X-Kizlo-Geo-Country"] = connInfo.country;
@@ -44,17 +44,21 @@ function getCartHeaders(options) {
44
44
  function sessionMiddleware(options) {
45
45
  const cookieName = options?.cookieName ?? "guest-session";
46
46
  const ttlSeconds = seconds(options?.ttl ?? "48 hours");
47
+ const cookieOptions = {
48
+ ...GUEST_COOKIE_OPTIONS,
49
+ maxAge: ttlSeconds
50
+ };
47
51
  return createMiddleware(async ({ context, next }) => {
48
52
  const connInfo = await context.getConnInfo();
49
- const auth = await context.getAuthUser();
53
+ const session = await context.getSession();
50
54
  const foundToken = await context.cookies.get(cookieName);
51
- if (!auth) {
55
+ if (!session) {
52
56
  if (!foundToken) {
53
57
  const { jwt, sub } = await mintGuestToken(context.config.siteSecret, ttlSeconds);
54
58
  await context.cookies.set({
55
59
  name: cookieName,
56
60
  value: jwt,
57
- options: GUEST_COOKIE_OPTIONS
61
+ options: cookieOptions
58
62
  });
59
63
  return next({ context: { sessionHeaders: getCartHeaders({
60
64
  token: sub,
@@ -67,7 +71,7 @@ function sessionMiddleware(options) {
67
71
  await context.cookies.set({
68
72
  name: cookieName,
69
73
  value: jwt,
70
- options: GUEST_COOKIE_OPTIONS
74
+ options: cookieOptions
71
75
  });
72
76
  throw new KizloError("CART_SESSION_EXPIRED");
73
77
  }
@@ -76,29 +80,28 @@ function sessionMiddleware(options) {
76
80
  connInfo
77
81
  }) } });
78
82
  }
79
- if (foundToken) {
83
+ let guestToken;
84
+ if (foundToken && options?.transitionGuestCart) {
80
85
  const [err, data] = await tryCatch(verifyToken(foundToken, context.config.siteSecret));
81
- if (!err) {
82
- const response = await context.wordpress.woocommerce.kizlo.cart.merge({}, { headers: getCartHeaders({
83
- userId: auth.id,
84
- token: data.sub,
85
- connInfo
86
- }) });
87
- if (response.error) context.logger.error("CART_MERGE_FAILED", response.error);
88
- }
89
- await context.cookies.delete(cookieName, GUEST_COOKIE_OPTIONS);
86
+ if (!err) guestToken = data.sub;
90
87
  }
91
- return next({ context: { sessionHeaders: getCartHeaders({
92
- userId: auth.id,
88
+ const result = await next({ context: { sessionHeaders: getCartHeaders({
89
+ email: session.email,
90
+ token: guestToken,
93
91
  connInfo
94
92
  }) } });
93
+ if (foundToken && options?.transitionGuestCart && context.headers) await context.cookies.delete(cookieName, GUEST_COOKIE_OPTIONS);
94
+ return result;
95
95
  });
96
96
  }
97
97
 
98
98
  //#endregion
99
99
  //#region src/cart/error.ts
100
100
  const GET_CART_ERROR_MAP = defineErrorMap({});
101
- const UPDATE_CART_ERROR_MAP = defineErrorMap({});
101
+ const UPDATE_CART_ERROR_MAP = defineErrorMap({ CART_ADDRESS_INVALID: {
102
+ status: 400,
103
+ message: "The cart address is invalid."
104
+ } });
102
105
  const ADD_CART_ITEM_ERROR_MAP = defineErrorMap({
103
106
  CART_ITEM_OUT_OF_STOCK: {
104
107
  status: 409,
@@ -191,351 +194,1015 @@ const REMOVE_COUPON_ERROR_MAP = defineErrorMap({
191
194
  });
192
195
 
193
196
  //#endregion
194
- //#region src/schema.ts
195
- const Totals = z$1.object({
196
- discountTotal: z$1.number(),
197
- discountTaxTotal: z$1.number(),
198
- shippingTotal: z$1.number(),
199
- shippingTaxTotal: z$1.number(),
200
- feeTotal: z$1.number(),
201
- feeTaxTotal: z$1.number(),
202
- taxTotal: z$1.number(),
203
- total: z$1.number()
204
- });
205
- const ItemTotals = z$1.object({
206
- unitPrice: z$1.number(),
207
- grossAmount: z$1.number(),
208
- discountAmount: z$1.number(),
209
- discountTaxAmount: z$1.number(),
210
- netAmount: z$1.number(),
211
- taxAmount: z$1.number(),
212
- total: z$1.number()
213
- });
214
- const ShippingAddress = z$1.object({
215
- firstName: z$1.string(),
216
- lastName: z$1.string(),
217
- phone: z$1.string(),
218
- company: z$1.string().optional(),
219
- address1: z$1.string(),
220
- address2: z$1.string().optional(),
221
- city: z$1.string(),
222
- postcode: z$1.string(),
223
- state: z$1.string(),
224
- country: z$1.string()
225
- });
226
- const BillingAddress = ShippingAddress.extend({ email: z$1.string() });
227
-
228
- //#endregion
229
- //#region src/cart/schema.ts
230
- const CART_ITEM_STATUSES = [
231
- "insufficient_stock",
232
- "low_stock",
233
- "out_of_stock",
234
- "unavailable",
235
- "available"
197
+ //#region src/product/schema.ts
198
+ const SWATCH_TYPES = [
199
+ "text",
200
+ "color",
201
+ "image"
236
202
  ];
237
- const CartLineItemStatus = z$1.enum(CART_ITEM_STATUSES);
238
- const PackageAddress = z$1.object({
239
- address1: z$1.string(),
240
- address2: z$1.string(),
241
- city: z$1.string(),
242
- state: z$1.string(),
243
- postcode: z$1.string(),
244
- country: z$1.string()
245
- });
246
- const CartPackageItem = z$1.object({
247
- key: z$1.string(),
248
- name: z$1.string(),
249
- quantity: z$1.number()
203
+ const SwatchType = z.enum(SWATCH_TYPES);
204
+ const PRODUCT_TYPES = [
205
+ "simple",
206
+ "grouped",
207
+ "external",
208
+ "variable",
209
+ "variation"
210
+ ];
211
+ const ProductType = z.enum(PRODUCT_TYPES);
212
+ const ProductTermSummary = z.object({
213
+ id: z.number(),
214
+ name: z.string(),
215
+ slug: z.string(),
216
+ url: z.string().nullable()
250
217
  });
251
- const CartPackageRate = z$1.object({
252
- id: z$1.string(),
253
- methodId: z$1.string(),
254
- name: z$1.string(),
255
- isSelected: z$1.boolean(),
256
- description: z$1.string(),
257
- deliveryTime: z$1.string(),
258
- amount: z$1.number(),
259
- taxAmount: z$1.number(),
260
- total: z$1.number()
218
+ const ProductTagSummary = ProductTermSummary;
219
+ const ProductBrandSummary = ProductTermSummary;
220
+ const ProductCategorySummary = ProductTermSummary;
221
+ const ProductAttributeTermSummary = z.object({
222
+ id: z.number(),
223
+ name: z.string(),
224
+ slug: z.string(),
225
+ isDefault: z.boolean()
261
226
  });
262
- const CartPackageLine = z$1.object({
263
- id: z$1.number(),
264
- name: z$1.string(),
265
- address: PackageAddress,
266
- items: z$1.array(CartPackageItem),
267
- rates: z$1.array(CartPackageRate)
227
+ const ProductAttributeSummary = z.object({
228
+ id: z.number(),
229
+ name: z.string(),
230
+ taxonomy: z.string().nullable(),
231
+ hasVariations: z.boolean(),
232
+ terms: z.array(ProductAttributeTermSummary)
268
233
  });
269
- const CartItemVariation = z$1.object({
270
- name: z$1.string(),
271
- attribute: z$1.string(),
272
- value: z$1.string()
234
+ const ProductVariationAttributeSummary = z.object({
235
+ name: z.string(),
236
+ value: z.string().nullable()
273
237
  });
274
- const CartItemPrices = z$1.object({
275
- price: z$1.number(),
276
- salePrice: z$1.number().nullable(),
277
- regularPrice: z$1.number()
238
+ const ProductVariationSummary = z.object({
239
+ id: z.number(),
240
+ attributes: z.array(ProductVariationAttributeSummary)
278
241
  });
279
- const CartItem = z$1.object({
280
- key: z$1.string(),
281
- type: z$1.string(),
282
- status: CartLineItemStatus,
283
- productId: z$1.number(),
284
- variationId: z$1.number().nullable(),
285
- name: z$1.string(),
286
- description: z$1.string(),
287
- shortDescription: z$1.string(),
288
- sku: z$1.string(),
289
- slug: z$1.string(),
290
- lowStockCount: z$1.number().nullable(),
291
- isSoldIndividually: z$1.boolean(),
292
- images: z$1.array(MediaImage),
293
- variations: z$1.array(CartItemVariation),
294
- prices: CartItemPrices,
295
- quantity: z$1.number(),
296
- totals: ItemTotals
242
+ const ProductPriceRange = z.object({
243
+ minAmount: z.number(),
244
+ maxAmount: z.number()
297
245
  });
298
- const AddCartItemInput = z$1.object({
299
- productId: z$1.number(),
300
- quantity: z$1.number(),
301
- variations: z$1.array(z$1.object({
302
- attribute: z$1.string(),
303
- value: z$1.string()
304
- })).optional()
246
+ const ProductPrices = z.object({
247
+ price: z.number(),
248
+ regularPrice: z.number(),
249
+ salePrice: z.number().nullable(),
250
+ priceRange: ProductPriceRange.nullable()
305
251
  });
306
- const UpdateCartItemInput = z$1.object({
307
- key: z$1.string(),
308
- quantity: z$1.number()
252
+ const ProductStockAvailability = z.object({
253
+ text: z.string(),
254
+ class: z.string()
309
255
  });
310
- const RemoveCartItemInput = z$1.object({ key: z$1.string() });
311
- const CartCouponLine = z$1.object({
312
- id: z$1.string(),
313
- type: z$1.string(),
314
- code: z$1.string(),
315
- amount: z$1.number(),
316
- taxAmount: z$1.number()
256
+ const ProductDimensions = z.object({
257
+ length: z.string(),
258
+ width: z.string(),
259
+ height: z.string()
317
260
  });
318
- const ApplyCouponInput = z$1.object({ code: z$1.string() });
319
- const RemoveCouponInput = z$1.object({ code: z$1.string() });
320
- const CartShippingLine = z$1.object({
321
- id: z$1.string(),
322
- label: z$1.string(),
323
- amount: z$1.number(),
324
- taxAmount: z$1.number()
261
+ const ProductAddToCart = z.object({
262
+ text: z.string(),
263
+ description: z.string(),
264
+ singleText: z.string(),
265
+ minimum: z.number(),
266
+ maximum: z.number(),
267
+ multipleOf: z.number()
325
268
  });
326
- const CartShippingAddress = ShippingAddress.extend({ id: z$1.string().optional() });
327
- const CartBillingAddress = BillingAddress.extend({ id: z$1.string().optional() });
328
- const Cart = z$1.object({
329
- totalItems: z$1.number(),
330
- lineItems: z$1.array(CartItem),
331
- billing: CartBillingAddress.nullable(),
332
- shipping: CartShippingAddress.nullable(),
333
- packageLines: z$1.array(CartPackageLine),
334
- couponLines: z$1.array(CartCouponLine),
335
- shippingLines: z$1.array(CartShippingLine),
269
+ const ProductExtensions = z.record(z.string(), z.unknown());
270
+ const ProductSummary = z.object({
271
+ id: z.number(),
272
+ name: z.string(),
273
+ slug: z.string(),
274
+ parentId: z.number().nullable(),
275
+ type: z.string(),
276
+ variationDescription: z.string(),
277
+ url: z.string().nullable(),
278
+ sku: z.string().nullable(),
279
+ shortDescription: z.string(),
280
+ description: z.string(),
281
+ isPasswordProtected: z.boolean(),
282
+ isOnSale: z.boolean(),
283
+ prices: ProductPrices,
336
284
  currencyFormat: CurrencyFormat,
337
- totals: Totals
285
+ priceHtml: z.string(),
286
+ averageRating: z.number(),
287
+ reviewCount: z.number(),
288
+ images: z.array(MediaImage),
289
+ categories: z.array(ProductCategorySummary),
290
+ tags: z.array(ProductTagSummary),
291
+ brands: z.array(ProductBrandSummary),
292
+ attributes: z.array(ProductAttributeSummary),
293
+ variations: z.array(ProductVariationSummary),
294
+ groupedProductIds: z.array(z.number()),
295
+ hasOptions: z.boolean(),
296
+ isPurchasable: z.boolean(),
297
+ isInStock: z.boolean(),
298
+ isOnBackorder: z.boolean(),
299
+ stockAvailability: ProductStockAvailability,
300
+ lowStockRemaining: z.number().nullable(),
301
+ isSoldIndividually: z.boolean(),
302
+ addToCart: ProductAddToCart,
303
+ extensions: ProductExtensions
338
304
  });
339
- const SelectCartShippingRateInput = z$1.object({
340
- rateId: z$1.string(),
341
- packageId: z$1.number()
305
+ const ProductRecommendations = z.object({
306
+ upsells: z.array(ProductSummary),
307
+ crossSells: z.array(ProductSummary),
308
+ related: z.array(ProductSummary)
342
309
  });
343
- const AddressInput = (schema) => {
344
- return schema.extend({ id: z$1.string().optional() });
345
- };
346
- const UpdateCartInput = z$1.object({
347
- shipping: AddressInput(ShippingAddress).nullable().optional(),
348
- billing: AddressInput(BillingAddress).nullable().optional()
310
+ const ProductCustomFieldsSchema = customFieldsSchema();
311
+ const Product = ProductSummary.extend({
312
+ weight: z.string(),
313
+ dimensions: ProductDimensions,
314
+ formattedWeight: z.string(),
315
+ formattedDimensions: z.string(),
316
+ stockQuantity: z.number().nullable(),
317
+ saleStartsAt: z.number().nullable(),
318
+ saleEndsAt: z.number().nullable(),
319
+ seo: Seo.nullable(),
320
+ custom: ProductCustomFieldsSchema,
321
+ recommendations: ProductRecommendations.nullable()
349
322
  });
350
-
351
- //#endregion
352
- //#region src/cart/utils.ts
353
- function deserializeCart(data) {
354
- const lineItems = data.items.map((item) => {
355
- const totals = calculateLineItemTotals({
356
- quantity: item.quantity,
357
- subtotal: Number(item.totals.line_subtotal),
358
- subtotal_tax: Number(item.totals.line_subtotal_tax),
359
- total: Number(item.totals.line_total),
360
- total_tax: Number(item.totals.line_total_tax)
361
- });
362
- let itemStatus = "available";
363
- const error = data.errors.find((e) => e.message.includes(item.name));
364
- if (error) switch (error.code) {
365
- case "woocommerce_rest_product_out_of_stock":
366
- itemStatus = "out_of_stock";
367
- break;
368
- case "woocommerce_rest_cart_item_error":
369
- itemStatus = "unavailable";
370
- break;
371
- }
372
- if (item.low_stock_remaining) itemStatus = "low_stock";
373
- const productSlug = new URL(item.permalink).pathname;
374
- return {
375
- key: item.key,
376
- productId: item.id,
377
- variationId: null,
378
- type: item.type,
379
- name: item.name,
380
- sku: item.sku,
381
- status: itemStatus,
382
- quantity: item.quantity,
383
- isSoldIndividually: item.sold_individually,
384
- images: item.images.map((item$1) => ({
385
- type: "image",
386
- id: item$1.id,
387
- name: item$1.name,
388
- alt: item$1.alt,
389
- src: item$1.src
390
- })),
391
- variations: item.variation.map((a) => ({
392
- name: a.attribute,
393
- attribute: a.raw_attribute,
394
- value: a.value
395
- })),
396
- slug: productSlug,
397
- description: item.description,
398
- soldIndividually: item.sold_individually,
399
- lowStockCount: item.low_stock_remaining,
400
- prices: {
401
- price: Number(item.prices.price),
402
- salePrice: Number(item.prices.sale_price),
403
- regularPrice: Number(item.prices.regular_price)
404
- },
405
- totals,
406
- shortDescription: item.short_description
407
- };
408
- });
409
- const couponLines = data.coupons.map((coupon) => {
410
- return {
411
- id: coupon.code,
412
- code: coupon.code,
413
- type: coupon.discount_type,
414
- amount: Number(coupon.totals.total_discount),
415
- taxAmount: Number(coupon.totals.total_discount_tax)
416
- };
417
- });
418
- const packageLines = data.shipping_rates.map((pkg) => ({
419
- id: Number(pkg.package_id),
420
- name: pkg.name,
421
- address: {
422
- address1: pkg.destination.address_1,
423
- address2: pkg.destination.address_2,
424
- city: pkg.destination.city,
425
- state: pkg.destination.state,
426
- postcode: pkg.destination.postcode,
427
- country: pkg.destination.country
428
- },
429
- items: pkg.items.map((item) => ({
430
- key: item.key,
431
- name: item.name,
432
- quantity: item.quantity
433
- })),
434
- rates: pkg.shipping_rates.map((rate) => ({
435
- id: rate.rate_id,
436
- name: rate.name,
437
- description: rate.description,
438
- deliveryTime: rate.delivery_time,
439
- amount: Number(rate.price),
440
- taxAmount: Number(rate.taxes),
441
- total: Number(rate.price) + Number(rate.taxes),
442
- isSelected: rate.selected,
443
- methodId: rate.method_id
444
- }))
445
- }));
446
- const shippingLines = packageLines.reduce((acc, item) => {
447
- const found = item.rates.find((a) => a.isSelected);
448
- if (found) acc.push({
449
- id: found.id,
450
- label: found.name,
451
- amount: found.amount,
452
- taxAmount: found.taxAmount
453
- });
454
- return acc;
455
- }, []);
456
- const discountTotal = Number(data.totals.total_discount);
457
- const discountTaxTotal = Number(data.totals.total_discount_tax);
458
- const shippingTotal = Number(data.totals.total_shipping);
459
- const shippingTaxTotal = Number(data.totals.total_shipping_tax);
460
- const feeTotal = Number(data.totals.total_fees);
461
- const feeTaxTotal = Number(data.totals.total_fees_tax);
462
- const taxTotal = Number(data.totals.total_tax);
463
- const total = Number(data.totals.total_price);
464
- return {
465
- lineItems,
466
- couponLines,
467
- packageLines,
468
- shippingLines,
469
- totalItems: data.items_count,
470
- billing: data.billing_address.address_1.length ? deserializeCartBillingAddress(data) : null,
471
- shipping: data.shipping_address.address_1.length ? deserializeCartShippingAddress(data) : null,
472
- currencyFormat: deserializeCurrencyFormat(data.totals),
473
- totals: {
474
- discountTotal,
475
- discountTaxTotal,
476
- shippingTotal,
477
- shippingTaxTotal,
478
- feeTotal,
479
- feeTaxTotal,
480
- taxTotal,
481
- total
482
- }
483
- };
484
- }
485
- function deserializeCartShippingAddress(cart) {
486
- return {
487
- address1: cart.shipping_address.address_1,
488
- address2: cart.shipping_address.address_2,
489
- city: cart.shipping_address.city,
490
- company: cart.shipping_address.company,
491
- country: cart.shipping_address.country,
492
- firstName: cart.shipping_address.first_name,
493
- lastName: cart.shipping_address.last_name,
494
- phone: cart.shipping_address.phone,
495
- postcode: cart.shipping_address.postcode,
496
- state: cart.shipping_address.state
497
- };
498
- }
499
- function deserializeCartBillingAddress(cart) {
500
- return {
501
- address1: cart.billing_address.address_1,
502
- address2: cart.billing_address.address_2,
503
- city: cart.billing_address.city,
504
- company: cart.billing_address.company,
505
- country: cart.billing_address.country,
506
- firstName: cart.billing_address.first_name,
507
- lastName: cart.billing_address.last_name,
508
- phone: cart.billing_address.phone,
509
- postcode: cart.billing_address.postcode,
510
- state: cart.billing_address.state,
511
- email: cart.billing_address.email
512
- };
513
- }
514
- function calculateLineItemTotals(input) {
515
- const unitPrice = input.subtotal / input.quantity;
516
- const grossAmount = input.subtotal;
517
- return {
518
- unitPrice,
519
- discountAmount: input.subtotal - input.total,
520
- discountTaxAmount: input.subtotal_tax - input.total_tax,
521
- grossAmount,
522
- netAmount: input.total,
523
- taxAmount: input.total_tax,
524
- total: input.total + input.total_tax
525
- };
526
- }
527
-
528
- //#endregion
529
- //#region src/cart/index.ts
530
- const CART_PROCEDURES = {
531
- get: createProcedure({
532
- scope: "api",
533
- method: "GET",
534
- path: "/cart",
535
- output: Cart.nullable(),
536
- errors: GET_CART_ERROR_MAP,
537
- middlewares: [sessionMiddleware()]
538
- }, async ({ context, errors }) => {
323
+ const ProductList = z.object({
324
+ items: z.array(Product),
325
+ meta: ListMetadata
326
+ });
327
+ const RetrieveProductInput = z.object({
328
+ identifier: IdentifierInput,
329
+ previewToken: z.string().optional(),
330
+ recommendations: BooleanLike.optional()
331
+ });
332
+ const PRODUCTS_ORDER_BYS = [
333
+ "date",
334
+ "modified",
335
+ "id",
336
+ "include",
337
+ "title",
338
+ "slug",
339
+ "price",
340
+ "popularity",
341
+ "rating",
342
+ "menu_order",
343
+ "comment_count"
344
+ ];
345
+ const ProductOrderBy = z.enum(PRODUCTS_ORDER_BYS);
346
+ const PRODUCT_DATE_COLUMNS = [
347
+ "date",
348
+ "date_gmt",
349
+ "modified",
350
+ "modified_gmt"
351
+ ];
352
+ const ProductDateColumn = z.enum(PRODUCT_DATE_COLUMNS);
353
+ const PRODUCT_TAXONOMY_OPERATORS = [
354
+ "in",
355
+ "not_in",
356
+ "and"
357
+ ];
358
+ const ProductTaxonomyOperator = z.enum(PRODUCT_TAXONOMY_OPERATORS);
359
+ const PRODUCT_ATTRIBUTE_RELATIONS = ["in", "and"];
360
+ const ProductAttributeRelation = z.enum(PRODUCT_ATTRIBUTE_RELATIONS);
361
+ const PRODUCT_STOCK_STATUSES = [
362
+ "instock",
363
+ "outofstock",
364
+ "onbackorder"
365
+ ];
366
+ const ProductStockStatus = z.enum(PRODUCT_STOCK_STATUSES);
367
+ const PRODUCT_CATALOG_VISIBILITIES = [
368
+ "any",
369
+ "visible",
370
+ "catalog",
371
+ "search",
372
+ "hidden"
373
+ ];
374
+ const ProductCatalogVisibility = z.enum(PRODUCT_CATALOG_VISIBILITIES);
375
+ const PRODUCT_RATINGS = [
376
+ 1,
377
+ 2,
378
+ 3,
379
+ 4,
380
+ 5
381
+ ];
382
+ const ProductRating = z.union([
383
+ z.literal(1),
384
+ z.literal(2),
385
+ z.literal(3),
386
+ z.literal(4),
387
+ z.literal(5)
388
+ ]);
389
+ const ProductRatingInput = NumberLike.pipe(ProductRating);
390
+ const ProductTermIdentifier = z.union([NumberLike, z.string()]);
391
+ const ProductTaxonomyName = z.string().min(1).regex(/^[a-z0-9_-]+$/).refine((name) => !name.endsWith("_operator"));
392
+ const ProductAttributeFilter = z.object({
393
+ taxonomy: z.string(),
394
+ slug: arrayable(z.string()).optional(),
395
+ termId: arrayable(NumberLike).optional(),
396
+ operator: ProductTaxonomyOperator.optional()
397
+ });
398
+ const ProductTaxonomyFilter = z.object({
399
+ taxonomy: ProductTaxonomyName,
400
+ termIds: arrayable(NumberLike).optional(),
401
+ slugs: arrayable(z.string()).optional(),
402
+ operator: ProductTaxonomyOperator.optional()
403
+ }).refine((filter) => filter.termIds === void 0 !== (filter.slugs === void 0), { message: "Provide either termIds or slugs." });
404
+ const ListProductInput = z.object({
405
+ page: NumberLike.optional(),
406
+ perPage: NumberLike.optional(),
407
+ search: z.string().optional(),
408
+ recommendations: BooleanLike.optional(),
409
+ slug: arrayable(z.string()).optional(),
410
+ after: z.string().optional(),
411
+ before: z.string().optional(),
412
+ dateColumn: ProductDateColumn.optional(),
413
+ exclude: arrayable(NumberLike).optional(),
414
+ include: arrayable(NumberLike).optional(),
415
+ offset: NumberLike.optional(),
416
+ order: z.enum(["asc", "desc"]).optional(),
417
+ orderBy: ProductOrderBy.optional(),
418
+ parent: arrayable(NumberLike).optional(),
419
+ parentExclude: arrayable(NumberLike).optional(),
420
+ type: z.string().optional(),
421
+ sku: arrayable(z.string()).optional(),
422
+ featured: BooleanLike.optional(),
423
+ category: arrayable(ProductTermIdentifier).optional(),
424
+ categoryOperator: ProductTaxonomyOperator.optional(),
425
+ brand: arrayable(ProductTermIdentifier).optional(),
426
+ brandOperator: ProductTaxonomyOperator.optional(),
427
+ tag: arrayable(ProductTermIdentifier).optional(),
428
+ tagOperator: ProductTaxonomyOperator.optional(),
429
+ onSale: BooleanLike.optional(),
430
+ minPrice: NumberLike.optional(),
431
+ maxPrice: NumberLike.optional(),
432
+ stockStatus: arrayable(ProductStockStatus).optional(),
433
+ attributes: z.array(ProductAttributeFilter).optional(),
434
+ attributeRelation: ProductAttributeRelation.optional(),
435
+ catalogVisibility: ProductCatalogVisibility.optional(),
436
+ rating: arrayable(ProductRatingInput).optional(),
437
+ related: NumberLike.optional(),
438
+ taxonomies: z.array(ProductTaxonomyFilter).optional()
439
+ });
440
+ const ProductFiltersPriceRange = z.object({
441
+ minPrice: z.number(),
442
+ maxPrice: z.number()
443
+ });
444
+ const ProductFiltersStockStatus = z.object({
445
+ count: z.number(),
446
+ status: ProductStockStatus
447
+ });
448
+ const ProductFiltersRatingCount = z.object({
449
+ count: z.number(),
450
+ rating: ProductRating
451
+ });
452
+ const ProductFiltersTerm = z.object({
453
+ id: z.number(),
454
+ parentId: z.number().nullable(),
455
+ name: z.string(),
456
+ slug: z.string(),
457
+ taxonomy: z.string(),
458
+ description: z.string(),
459
+ count: z.number()
460
+ });
461
+ const ProductFiltersTaxonomyTerm = ProductFiltersTerm.extend({ image: MediaImage.nullable() });
462
+ const ProductFiltersAttributeTerm = ProductFiltersTerm.extend({
463
+ type: SwatchType,
464
+ swatch: z.string().nullable()
465
+ });
466
+ const ProductFilters = z.object({
467
+ priceRange: ProductFiltersPriceRange,
468
+ ratingCounts: z.array(ProductFiltersRatingCount),
469
+ stockStatuses: z.array(ProductFiltersStockStatus),
470
+ attributeTerms: z.array(ProductFiltersAttributeTerm),
471
+ taxonomyTerms: z.array(ProductFiltersTaxonomyTerm),
472
+ currencyFormat: CurrencyFormat
473
+ });
474
+ const ProductAttributeCount = z.object({
475
+ taxonomy: z.string(),
476
+ operator: z.enum(["or", "and"]).optional()
477
+ });
478
+ const RetrieveProductFiltersInput = ListProductInput.omit({ recommendations: true }).extend({
479
+ ratingCounts: BooleanLike.optional(),
480
+ stockStatusCounts: BooleanLike.optional(),
481
+ taxonomyCounts: z.array(z.string()).optional(),
482
+ attributeCounts: z.array(ProductAttributeCount).optional()
483
+ });
484
+
485
+ //#endregion
486
+ //#region src/cart/schema.ts
487
+ const CartAdditionalFields = z$1.record(z$1.string(), z$1.union([z$1.string(), z$1.boolean()]));
488
+ const CartAddressFields = {
489
+ firstName: z$1.string(),
490
+ lastName: z$1.string(),
491
+ company: z$1.string(),
492
+ address1: z$1.string(),
493
+ address2: z$1.string(),
494
+ city: z$1.string(),
495
+ state: z$1.string(),
496
+ postcode: z$1.string(),
497
+ country: z$1.string(),
498
+ phone: z$1.string(),
499
+ additionalFields: CartAdditionalFields
500
+ };
501
+ const CartShippingAddress = z$1.object(CartAddressFields);
502
+ const CartBillingAddress = z$1.object({
503
+ ...CartAddressFields,
504
+ email: z$1.string()
505
+ });
506
+ const CartShippingDestination = z$1.object({
507
+ address1: z$1.string(),
508
+ address2: z$1.string(),
509
+ city: z$1.string(),
510
+ state: z$1.string(),
511
+ postcode: z$1.string(),
512
+ country: z$1.string()
513
+ });
514
+ const CartShippingItem = z$1.object({
515
+ key: z$1.string(),
516
+ name: z$1.string(),
517
+ quantity: z$1.number()
518
+ });
519
+ const CartShippingRateMetadata = z$1.object({
520
+ key: z$1.string(),
521
+ value: z$1.string()
522
+ });
523
+ const CartShippingRate = z$1.object({
524
+ id: z$1.string(),
525
+ name: z$1.string(),
526
+ description: z$1.string(),
527
+ deliveryTime: z$1.string(),
528
+ price: z$1.number(),
529
+ taxes: z$1.number(),
530
+ methodId: z$1.string(),
531
+ instanceId: z$1.number(),
532
+ metadata: z$1.array(CartShippingRateMetadata),
533
+ selected: z$1.boolean()
534
+ });
535
+ const CartShippingPackage = z$1.object({
536
+ id: z$1.union([z$1.number(), z$1.string()]),
537
+ name: z$1.string(),
538
+ destination: CartShippingDestination,
539
+ items: z$1.array(CartShippingItem),
540
+ rates: z$1.array(CartShippingRate)
541
+ });
542
+ const CartSelectedAttribute = z$1.object({
543
+ name: z$1.string(),
544
+ attribute: z$1.string(),
545
+ value: z$1.string()
546
+ });
547
+ const CartItemData = z$1.object({
548
+ name: z$1.string(),
549
+ value: z$1.string(),
550
+ display: z$1.string().nullable()
551
+ });
552
+ const CartItemQuantityLimits = z$1.object({
553
+ minimum: z$1.number(),
554
+ maximum: z$1.number(),
555
+ multipleOf: z$1.number(),
556
+ editable: z$1.boolean()
557
+ });
558
+ const CartItemTotals = z$1.object({
559
+ subtotal: z$1.number(),
560
+ subtotalTax: z$1.number(),
561
+ total: z$1.number(),
562
+ totalTax: z$1.number()
563
+ });
564
+ const CartItem = z$1.object({
565
+ key: z$1.string(),
566
+ productId: z$1.number(),
567
+ variationId: z$1.number().nullable(),
568
+ type: z$1.string(),
569
+ name: z$1.string(),
570
+ sku: z$1.string().nullable(),
571
+ slug: z$1.string(),
572
+ url: z$1.string().nullable(),
573
+ shortDescription: z$1.string(),
574
+ description: z$1.string(),
575
+ quantity: z$1.number(),
576
+ quantityLimits: CartItemQuantityLimits,
577
+ lowStockRemaining: z$1.number().nullable(),
578
+ allowsBackorders: z$1.boolean(),
579
+ showsBackorderBadge: z$1.boolean(),
580
+ isSoldIndividually: z$1.boolean(),
581
+ catalogVisibility: z$1.string(),
582
+ images: z$1.array(MediaImage),
583
+ selectedAttributes: z$1.array(CartSelectedAttribute),
584
+ itemData: z$1.array(CartItemData),
585
+ prices: ProductPrices,
586
+ totals: CartItemTotals,
587
+ custom: ProductCustomFieldsSchema,
588
+ extensions: z$1.record(z$1.string(), z$1.unknown())
589
+ });
590
+ const CartCouponTotals = z$1.object({
591
+ discount: z$1.number(),
592
+ discountTax: z$1.number()
593
+ });
594
+ const CartCoupon = z$1.object({
595
+ code: z$1.string(),
596
+ discountType: z$1.string(),
597
+ totals: CartCouponTotals
598
+ });
599
+ const CartFeeTotals = z$1.object({
600
+ total: z$1.number(),
601
+ tax: z$1.number()
602
+ });
603
+ const CartFee = z$1.object({
604
+ id: z$1.string(),
605
+ name: z$1.string(),
606
+ totals: CartFeeTotals
607
+ });
608
+ const CartTaxLine = z$1.object({
609
+ name: z$1.string(),
610
+ price: z$1.number(),
611
+ rate: z$1.string()
612
+ });
613
+ const CartTotals = z$1.object({
614
+ itemsTotal: z$1.number(),
615
+ itemsTaxTotal: z$1.number(),
616
+ feesTotal: z$1.number(),
617
+ feesTaxTotal: z$1.number(),
618
+ discountTotal: z$1.number(),
619
+ discountTaxTotal: z$1.number(),
620
+ shippingTotal: z$1.number().nullable(),
621
+ shippingTaxTotal: z$1.number().nullable(),
622
+ total: z$1.number(),
623
+ taxTotal: z$1.number(),
624
+ taxLines: z$1.array(CartTaxLine)
625
+ });
626
+ const CartError = z$1.object({
627
+ code: z$1.string(),
628
+ message: z$1.string()
629
+ });
630
+ const CartPaymentMethod = z$1.object({
631
+ id: z$1.string(),
632
+ title: z$1.string(),
633
+ description: z$1.string(),
634
+ order: z$1.number(),
635
+ enabled: z$1.boolean()
636
+ });
637
+ const Cart = z$1.object({
638
+ items: z$1.array(CartItem),
639
+ itemCount: z$1.number(),
640
+ itemsWeight: z$1.number(),
641
+ billingAddress: CartBillingAddress,
642
+ shippingAddress: CartShippingAddress,
643
+ shippingPackages: z$1.array(CartShippingPackage),
644
+ coupons: z$1.array(CartCoupon),
645
+ fees: z$1.array(CartFee),
646
+ crossSells: z$1.array(ProductSummary),
647
+ needsPayment: z$1.boolean(),
648
+ needsShipping: z$1.boolean(),
649
+ hasCalculatedShipping: z$1.boolean(),
650
+ paymentMethods: z$1.array(CartPaymentMethod),
651
+ paymentRequirements: z$1.array(z$1.string()),
652
+ errors: z$1.array(CartError),
653
+ totals: CartTotals,
654
+ currencyFormat: CurrencyFormat,
655
+ extensions: z$1.record(z$1.string(), z$1.unknown())
656
+ });
657
+ const AddCartItemInput = z$1.object({
658
+ productId: z$1.number(),
659
+ variationId: z$1.number().optional(),
660
+ quantity: z$1.number().optional(),
661
+ selectedAttributes: z$1.array(z$1.object({
662
+ attribute: z$1.string(),
663
+ value: z$1.string()
664
+ })).optional()
665
+ });
666
+ const UpdateCartItemInput = z$1.object({
667
+ key: z$1.string(),
668
+ quantity: z$1.number()
669
+ });
670
+ const RemoveCartItemInput = z$1.object({ key: z$1.string() });
671
+ const ApplyCouponInput = z$1.object({ code: z$1.string() });
672
+ const RemoveCouponInput = z$1.object({ code: z$1.string() });
673
+ const SelectCartShippingRateInput = z$1.object({
674
+ rateId: z$1.string(),
675
+ packageId: z$1.union([z$1.number(), z$1.string()]).nullable().optional()
676
+ });
677
+ const CartShippingAddressInput = z$1.object(CartAddressFields).partial();
678
+ const CartBillingAddressInput = z$1.object({
679
+ ...CartAddressFields,
680
+ email: z$1.string()
681
+ }).partial();
682
+ const UpdateCartInput = z$1.object({
683
+ shippingAddress: CartShippingAddressInput.optional(),
684
+ billingAddress: CartBillingAddressInput.optional()
685
+ });
686
+
687
+ //#endregion
688
+ //#region src/product/utils.ts
689
+ function assertNoMissingStoreProductInputs() {}
690
+ assertNoMissingStoreProductInputs();
691
+ function deserializeProduct(data) {
692
+ return deserializeStoreProduct(data.kizlo.store_product, null);
693
+ }
694
+ function deserializeStoreProduct(data, recommendations) {
695
+ const summary = deserializeProductSummary(data);
696
+ const { kizlo } = deserializeExtensions(data.extensions);
697
+ return {
698
+ ...summary,
699
+ weight: data.weight,
700
+ dimensions: data.dimensions,
701
+ formattedWeight: data.formatted_weight,
702
+ formattedDimensions: data.formatted_dimensions,
703
+ stockQuantity: nullableNumber(kizlo.stock),
704
+ saleStartsAt: timestampFromIso(typeof kizlo.on_sale_from === "string" ? kizlo.on_sale_from : null),
705
+ saleEndsAt: timestampFromIso(typeof kizlo.on_sale_to === "string" ? kizlo.on_sale_to : null),
706
+ seo: isRecord$1(kizlo.seo) ? deserializeSeo(kizlo.seo) : null,
707
+ custom: productCustomFields(kizlo.custom),
708
+ recommendations
709
+ };
710
+ }
711
+ function deserializeProductSummary(data) {
712
+ const { extensions, kizlo } = deserializeExtensions(data.extensions);
713
+ const termUrls = deserializeTermUrls(kizlo.term_urls);
714
+ return {
715
+ id: data.id,
716
+ name: data.name,
717
+ slug: data.slug,
718
+ parentId: data.parent === 0 ? null : data.parent,
719
+ type: data.type,
720
+ variationDescription: data.variation,
721
+ url: typeof kizlo.url === "string" ? kizlo.url : null,
722
+ sku: data.sku === "" ? null : data.sku,
723
+ shortDescription: data.short_description,
724
+ description: data.description,
725
+ isPasswordProtected: data.is_password_protected,
726
+ isOnSale: data.on_sale,
727
+ prices: {
728
+ price: Number(data.prices.price),
729
+ regularPrice: Number(data.prices.regular_price),
730
+ salePrice: data.on_sale ? Number(data.prices.sale_price) : null,
731
+ priceRange: data.prices.price_range ? {
732
+ minAmount: Number(data.prices.price_range.min_amount),
733
+ maxAmount: Number(data.prices.price_range.max_amount)
734
+ } : null
735
+ },
736
+ currencyFormat: deserializeCurrencyFormat(data.prices),
737
+ priceHtml: data.price_html,
738
+ averageRating: Number(data.average_rating),
739
+ reviewCount: data.review_count,
740
+ images: data.images.map((image) => ({
741
+ type: "image",
742
+ id: image.id,
743
+ src: image.src,
744
+ srcset: image.srcset,
745
+ name: image.name,
746
+ alt: image.alt
747
+ })),
748
+ categories: data.categories.map((term) => deserializeTermRef(term, "product_cat", termUrls)),
749
+ tags: data.tags.map((term) => deserializeTermRef(term, "product_tag", termUrls)),
750
+ brands: data.brands.map((term) => deserializeTermRef(term, "product_brand", termUrls)),
751
+ attributes: data.attributes.map((attribute) => ({
752
+ id: attribute.id,
753
+ name: attribute.name,
754
+ taxonomy: attribute.taxonomy ?? null,
755
+ hasVariations: attribute.has_variations,
756
+ terms: attribute.terms.map((term) => ({
757
+ id: term.id,
758
+ name: term.name,
759
+ slug: term.slug,
760
+ isDefault: term.default ?? false
761
+ }))
762
+ })),
763
+ variations: data.variations.map((variation) => ({
764
+ id: variation.id,
765
+ attributes: variation.attributes.map((attribute) => ({
766
+ name: attribute.name,
767
+ value: attribute.value ?? null
768
+ }))
769
+ })),
770
+ groupedProductIds: data.grouped_products,
771
+ hasOptions: data.has_options,
772
+ isPurchasable: data.is_purchasable,
773
+ isInStock: data.is_in_stock,
774
+ isOnBackorder: data.is_on_backorder,
775
+ stockAvailability: data.stock_availability,
776
+ lowStockRemaining: data.low_stock_remaining,
777
+ isSoldIndividually: data.sold_individually,
778
+ addToCart: {
779
+ text: data.add_to_cart.text,
780
+ description: data.add_to_cart.description,
781
+ singleText: data.add_to_cart.single_text,
782
+ minimum: data.add_to_cart.minimum,
783
+ maximum: data.add_to_cart.maximum,
784
+ multipleOf: data.add_to_cart.multiple_of
785
+ },
786
+ extensions
787
+ };
788
+ }
789
+ function deserializeProductRecommendations(data) {
790
+ return {
791
+ upsells: deserializeEmbeddedProducts(data._embedded?.upsells),
792
+ crossSells: deserializeEmbeddedProducts(data._embedded?.cross_sells),
793
+ related: deserializeEmbeddedProducts(data._embedded?.related)
794
+ };
795
+ }
796
+ function productCustomFields(value) {
797
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
798
+ }
799
+ function deserializeProductFilters(data) {
800
+ if (!data.price_range) return null;
801
+ const maxPrice = +data.price_range.max_price;
802
+ const minPrice = +data.price_range.min_price;
803
+ return {
804
+ ratingCounts: (data.rating_counts ?? []).flatMap((entry) => PRODUCT_RATINGS.includes(entry.rating) ? [{
805
+ count: entry.count,
806
+ rating: entry.rating
807
+ }] : []),
808
+ stockStatuses: (data.stock_status_counts ?? []).flatMap((entry) => {
809
+ const status = stockStatus(entry.status);
810
+ return status ? [{
811
+ count: entry.count,
812
+ status
813
+ }] : [];
814
+ }),
815
+ taxonomyTerms: data.kizlo.taxonomy_counts.map((item) => ({
816
+ id: item.id,
817
+ name: item.name,
818
+ count: item.count,
819
+ description: item.description,
820
+ parentId: item.parent,
821
+ slug: item.slug,
822
+ taxonomy: item.taxonomy,
823
+ image: item.image
824
+ })),
825
+ attributeTerms: data.kizlo.attribute_counts.map((item) => ({
826
+ id: item.id,
827
+ name: item.name,
828
+ count: item.count,
829
+ description: item.description,
830
+ parentId: item.parent,
831
+ slug: item.slug,
832
+ swatch: item.swatch,
833
+ type: item.swatch_type,
834
+ taxonomy: item.taxonomy
835
+ })),
836
+ priceRange: {
837
+ maxPrice,
838
+ minPrice
839
+ },
840
+ currencyFormat: deserializeCurrencyFormat(data.price_range)
841
+ };
842
+ }
843
+ function serializeProductListInput(data) {
844
+ const searchParams = {
845
+ after: data?.after,
846
+ attribute_relation: data?.attributeRelation,
847
+ before: data?.before,
848
+ brand: commaSeparated(data?.brand),
849
+ brand_operator: data?.brandOperator,
850
+ catalog_visibility: data?.catalogVisibility,
851
+ category: commaSeparated(data?.category),
852
+ category_operator: data?.categoryOperator,
853
+ date_column: data?.dateColumn,
854
+ featured: data?.featured,
855
+ max_price: data?.maxPrice === void 0 ? void 0 : String(data.maxPrice),
856
+ min_price: data?.minPrice === void 0 ? void 0 : String(data.minPrice),
857
+ on_sale: data?.onSale,
858
+ orderby: data?.orderBy,
859
+ parent: normalizeArrayableValue(data?.parent),
860
+ parent_exclude: normalizeArrayableValue(data?.parentExclude),
861
+ rating: normalizeArrayableValue(data?.rating),
862
+ sku: commaSeparated(data?.sku),
863
+ slug: commaSeparated(data?.slug),
864
+ stock_status: normalizeArrayableValue(data?.stockStatus),
865
+ tag: commaSeparated(data?.tag),
866
+ tag_operator: data?.tagOperator,
867
+ type: data?.type,
868
+ attributes: data?.attributes?.map((item) => ({
869
+ operator: item.operator,
870
+ attribute: item.taxonomy,
871
+ slug: normalizeArrayableValue(item.slug),
872
+ term_id: normalizeArrayableValue(item.termId)
873
+ })),
874
+ exclude: normalizeArrayableValue(data?.exclude),
875
+ include: normalizeArrayableValue(data?.include),
876
+ offset: data?.offset,
877
+ order: data?.order,
878
+ page: data?.page,
879
+ per_page: data?.perPage,
880
+ related: data?.related,
881
+ search: data?.search
882
+ };
883
+ for (const filter of data?.taxonomies ?? []) {
884
+ const key = `_unstable_tax_${filter.taxonomy}`;
885
+ searchParams[key] = commaSeparated(filter.termIds ?? filter.slugs);
886
+ if (filter.operator !== void 0) searchParams[`${key}_operator`] = filter.operator;
887
+ }
888
+ return searchParams;
889
+ }
890
+ function commaSeparated(value) {
891
+ if (value === void 0) return void 0;
892
+ return (Array.isArray(value) ? value : [value]).join(",");
893
+ }
894
+ function stockStatus(status) {
895
+ return PRODUCT_STOCK_STATUSES.includes(status) ? status : null;
896
+ }
897
+ function deserializeTermRef(term, taxonomy, urls) {
898
+ return {
899
+ id: term.id,
900
+ name: term.name,
901
+ slug: term.slug,
902
+ url: urls[`${taxonomy}:${term.id}`] ?? null
903
+ };
904
+ }
905
+ function deserializeTermUrls(value) {
906
+ if (!Array.isArray(value)) return {};
907
+ return Object.fromEntries(value.flatMap((item) => {
908
+ if (!isRecord$1(item) || typeof item.id !== "number" || typeof item.taxonomy !== "string" || typeof item.url !== "string") return [];
909
+ return [[`${item.taxonomy}:${item.id}`, item.url]];
910
+ }));
911
+ }
912
+ function deserializeEmbeddedProducts(collections) {
913
+ return (collections ?? []).flat().map(deserializeProductSummary);
914
+ }
915
+ function deserializeExtensions(value) {
916
+ const { kizlo: rawKizlo,...extensions } = asRecord(value);
917
+ return {
918
+ extensions,
919
+ kizlo: asRecord(rawKizlo)
920
+ };
921
+ }
922
+ function asRecord(value) {
923
+ return isRecord$1(value) ? value : {};
924
+ }
925
+ function isRecord$1(value) {
926
+ return typeof value === "object" && value !== null && !Array.isArray(value);
927
+ }
928
+ function nullableNumber(value) {
929
+ return typeof value === "number" ? value : null;
930
+ }
931
+
932
+ //#endregion
933
+ //#region src/cart/utils.ts
934
+ function assertNoMissing$2() {}
935
+ assertNoMissing$2();
936
+ assertNoMissing$2();
937
+ assertNoMissing$2();
938
+ assertNoMissing$2();
939
+ assertNoMissing$2();
940
+ assertNoMissing$2();
941
+ assertNoMissing$2();
942
+ assertNoMissing$2();
943
+ assertNoMissing$2();
944
+ assertNoMissing$2();
945
+ assertNoMissing$2();
946
+ assertNoMissing$2();
947
+ assertNoMissing$2();
948
+ assertNoMissing$2();
949
+ assertNoMissing$2();
950
+ assertNoMissing$2();
951
+ assertNoMissing$2();
952
+ assertNoMissing$2();
953
+ assertNoMissing$2();
954
+ assertNoMissing$2();
955
+ assertNoMissing$2();
956
+ function deserializeCart(data) {
957
+ const { extensions, kizlo } = deserializeExtensions(data.extensions);
958
+ return {
959
+ items: data.items.map(deserializeCartItem),
960
+ itemCount: data.items_count,
961
+ itemsWeight: data.items_weight,
962
+ billingAddress: deserializeCartBillingAddress(data.billing_address),
963
+ shippingAddress: deserializeCartShippingAddress(data.shipping_address),
964
+ shippingPackages: data.shipping_rates.map((pkg) => ({
965
+ id: pkg.package_id,
966
+ name: pkg.name,
967
+ destination: {
968
+ address1: pkg.destination.address_1,
969
+ address2: pkg.destination.address_2,
970
+ city: pkg.destination.city,
971
+ state: pkg.destination.state,
972
+ postcode: pkg.destination.postcode,
973
+ country: pkg.destination.country
974
+ },
975
+ items: pkg.items.map((item) => ({
976
+ key: item.key,
977
+ name: item.name,
978
+ quantity: item.quantity
979
+ })),
980
+ rates: pkg.shipping_rates.map((rate) => ({
981
+ id: rate.rate_id,
982
+ name: rate.name,
983
+ description: rate.description,
984
+ deliveryTime: rate.delivery_time,
985
+ price: Number(rate.price),
986
+ taxes: Number(rate.taxes),
987
+ methodId: rate.method_id,
988
+ instanceId: rate.instance_id,
989
+ metadata: rate.meta_data,
990
+ selected: rate.selected
991
+ }))
992
+ })),
993
+ coupons: data.coupons.map((coupon) => ({
994
+ code: coupon.code,
995
+ discountType: coupon.discount_type,
996
+ totals: {
997
+ discount: Number(coupon.totals.total_discount),
998
+ discountTax: Number(coupon.totals.total_discount_tax)
999
+ }
1000
+ })),
1001
+ fees: data.fees.map((fee) => ({
1002
+ id: fee.key,
1003
+ name: fee.name,
1004
+ totals: {
1005
+ total: Number(fee.totals.total),
1006
+ tax: Number(fee.totals.total_tax)
1007
+ }
1008
+ })),
1009
+ crossSells: data.cross_sells.map((product) => deserializeProductSummary(product)),
1010
+ needsPayment: data.needs_payment,
1011
+ needsShipping: data.needs_shipping,
1012
+ hasCalculatedShipping: data.has_calculated_shipping,
1013
+ paymentMethods: deserializePaymentMethods(kizlo.payment_methods),
1014
+ paymentRequirements: data.payment_requirements,
1015
+ errors: data.errors,
1016
+ totals: {
1017
+ itemsTotal: Number(data.totals.total_items),
1018
+ itemsTaxTotal: Number(data.totals.total_items_tax),
1019
+ feesTotal: Number(data.totals.total_fees),
1020
+ feesTaxTotal: Number(data.totals.total_fees_tax),
1021
+ discountTotal: Number(data.totals.total_discount),
1022
+ discountTaxTotal: Number(data.totals.total_discount_tax),
1023
+ shippingTotal: nullableMoney$1(data.totals.total_shipping),
1024
+ shippingTaxTotal: nullableMoney$1(data.totals.total_shipping_tax),
1025
+ total: Number(data.totals.total_price),
1026
+ taxTotal: Number(data.totals.total_tax),
1027
+ taxLines: data.totals.tax_lines.map((line) => ({
1028
+ name: line.name,
1029
+ price: Number(line.price),
1030
+ rate: line.rate
1031
+ }))
1032
+ },
1033
+ currencyFormat: deserializeCurrencyFormat(data.totals),
1034
+ extensions
1035
+ };
1036
+ }
1037
+ function deserializeCartItem(item) {
1038
+ const { extensions, kizlo } = deserializeExtensions(item.extensions);
1039
+ const variationId = typeof kizlo.variation_id === "number" ? kizlo.variation_id : 0;
1040
+ return {
1041
+ key: item.key,
1042
+ productId: typeof kizlo.product_id === "number" ? kizlo.product_id : item.id,
1043
+ variationId: variationId === 0 ? null : variationId,
1044
+ type: item.type,
1045
+ name: item.name,
1046
+ sku: item.sku === "" ? null : item.sku,
1047
+ slug: typeof kizlo.slug === "string" ? kizlo.slug : "",
1048
+ url: typeof kizlo.url === "string" ? kizlo.url : null,
1049
+ shortDescription: item.short_description,
1050
+ description: item.description,
1051
+ quantity: item.quantity,
1052
+ quantityLimits: {
1053
+ minimum: item.quantity_limits.minimum,
1054
+ maximum: item.quantity_limits.maximum,
1055
+ multipleOf: item.quantity_limits.multiple_of,
1056
+ editable: item.quantity_limits.editable
1057
+ },
1058
+ lowStockRemaining: item.low_stock_remaining,
1059
+ allowsBackorders: item.backorders_allowed,
1060
+ showsBackorderBadge: item.show_backorder_badge,
1061
+ isSoldIndividually: item.sold_individually,
1062
+ catalogVisibility: item.catalog_visibility,
1063
+ images: item.images.map((image) => ({
1064
+ type: "image",
1065
+ id: image.id,
1066
+ name: image.name,
1067
+ alt: image.alt,
1068
+ src: image.src,
1069
+ srcset: image.srcset
1070
+ })),
1071
+ selectedAttributes: item.variation.map((attribute) => ({
1072
+ name: attribute.attribute,
1073
+ attribute: attribute.raw_attribute,
1074
+ value: attribute.value
1075
+ })),
1076
+ itemData: item.item_data.map((entry) => ({
1077
+ name: entry.name,
1078
+ value: entry.value,
1079
+ display: entry.display ?? null
1080
+ })),
1081
+ prices: {
1082
+ price: Number(item.prices.price),
1083
+ regularPrice: Number(item.prices.regular_price),
1084
+ salePrice: item.prices.sale_price === "" || item.prices.sale_price === item.prices.regular_price ? null : Number(item.prices.sale_price),
1085
+ priceRange: item.prices.price_range ? {
1086
+ minAmount: Number(item.prices.price_range.min_amount),
1087
+ maxAmount: Number(item.prices.price_range.max_amount)
1088
+ } : null
1089
+ },
1090
+ totals: {
1091
+ subtotal: Number(item.totals.line_subtotal),
1092
+ subtotalTax: Number(item.totals.line_subtotal_tax),
1093
+ total: Number(item.totals.line_total),
1094
+ totalTax: Number(item.totals.line_total_tax)
1095
+ },
1096
+ custom: productCustomFields(kizlo.custom),
1097
+ extensions
1098
+ };
1099
+ }
1100
+ /**
1101
+ * Read the available payment gateways from the Kizlo cart extension.
1102
+ *
1103
+ * WooCommerce's native `payment_methods` is a list of IDs; the presentation
1104
+ * metadata lives on `extensions.kizlo.payment_methods`, which the WooCommerce
1105
+ * plugin populates. Entries missing a required field are dropped rather than
1106
+ * passed through half-formed.
1107
+ */
1108
+ function deserializePaymentMethods(value) {
1109
+ if (!Array.isArray(value)) return [];
1110
+ return value.flatMap((entry) => {
1111
+ if (typeof entry !== "object" || entry === null) return [];
1112
+ const { id, title, description, order, enabled } = entry;
1113
+ if (typeof id !== "string" || typeof title !== "string" || typeof description !== "string" || typeof order !== "number" || typeof enabled !== "boolean") return [];
1114
+ return [{
1115
+ id,
1116
+ title,
1117
+ description,
1118
+ order,
1119
+ enabled
1120
+ }];
1121
+ });
1122
+ }
1123
+ function deserializeCartShippingAddress(address) {
1124
+ return {
1125
+ firstName: address.first_name,
1126
+ lastName: address.last_name,
1127
+ company: address.company,
1128
+ address1: address.address_1,
1129
+ address2: address.address_2,
1130
+ city: address.city,
1131
+ state: address.state,
1132
+ postcode: address.postcode,
1133
+ country: address.country,
1134
+ phone: address.phone,
1135
+ additionalFields: additionalAddressFields(address, SHIPPING_ADDRESS_KEYS)
1136
+ };
1137
+ }
1138
+ function deserializeCartBillingAddress(address) {
1139
+ return {
1140
+ ...deserializeCartShippingAddress(address),
1141
+ email: address.email,
1142
+ additionalFields: additionalAddressFields(address, BILLING_ADDRESS_KEYS)
1143
+ };
1144
+ }
1145
+ const SHIPPING_ADDRESS_KEYS = new Set([
1146
+ "first_name",
1147
+ "last_name",
1148
+ "company",
1149
+ "address_1",
1150
+ "address_2",
1151
+ "city",
1152
+ "state",
1153
+ "postcode",
1154
+ "country",
1155
+ "phone"
1156
+ ]);
1157
+ const BILLING_ADDRESS_KEYS = new Set([...SHIPPING_ADDRESS_KEYS, "email"]);
1158
+ function additionalAddressFields(address, standardKeys) {
1159
+ return Object.fromEntries(Object.entries(address).filter((entry) => !standardKeys.has(entry[0]) && (typeof entry[1] === "string" || typeof entry[1] === "boolean")));
1160
+ }
1161
+ function serializeCartUpdateInput(input) {
1162
+ return {
1163
+ ...input.billingAddress !== void 0 && { billing_address: serializeCartBillingAddress(input.billingAddress) },
1164
+ ...input.shippingAddress !== void 0 && { shipping_address: serializeCartShippingAddress(input.shippingAddress) }
1165
+ };
1166
+ }
1167
+ function serializeCartShippingAddress(address) {
1168
+ return compactAddress({
1169
+ ...address.additionalFields,
1170
+ first_name: address.firstName,
1171
+ last_name: address.lastName,
1172
+ company: address.company,
1173
+ address_1: address.address1,
1174
+ address_2: address.address2,
1175
+ city: address.city,
1176
+ state: address.state,
1177
+ postcode: address.postcode,
1178
+ country: address.country,
1179
+ phone: address.phone
1180
+ });
1181
+ }
1182
+ function serializeCartBillingAddress(address) {
1183
+ return compactAddress({
1184
+ ...serializeCartShippingAddress(address),
1185
+ email: address.email
1186
+ });
1187
+ }
1188
+ function compactAddress(address) {
1189
+ return Object.fromEntries(Object.entries(address).filter(([, value]) => value !== void 0));
1190
+ }
1191
+ function nullableMoney$1(value) {
1192
+ return value === null ? null : Number(value);
1193
+ }
1194
+
1195
+ //#endregion
1196
+ //#region src/cart/index.ts
1197
+ const CART_PROCEDURES = {
1198
+ get: createProcedure({
1199
+ scope: "api",
1200
+ method: "GET",
1201
+ path: "/cart",
1202
+ output: Cart,
1203
+ errors: GET_CART_ERROR_MAP,
1204
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
1205
+ }, async ({ context, errors }) => {
539
1206
  const response = await context.wordpress.woocommerce.store.cart.get({}, { headers: context.sessionHeaders });
540
1207
  if (response.error) switch (response.error.code) {
541
1208
  default:
@@ -551,45 +1218,15 @@ const CART_PROCEDURES = {
551
1218
  output: Cart,
552
1219
  body: UpdateCartInput,
553
1220
  errors: UPDATE_CART_ERROR_MAP,
554
- middlewares: [sessionMiddleware()]
1221
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
555
1222
  }, async ({ context, input: { body: input }, errors }) => {
556
- const connInfo = await context.getConnInfo();
557
- const billing = input.billing ?? {
558
- ...input.shipping,
559
- email: void 0
560
- };
561
- const address = {
562
- first_name: billing.firstName ?? "",
563
- last_name: billing.lastName ?? "",
564
- address_1: billing.address1 ?? "",
565
- address_2: billing.address2 ?? "",
566
- company: billing.company ?? "",
567
- phone: billing.phone ?? "",
568
- city: billing.city ?? "",
569
- state: billing.state ?? connInfo?.state ?? "",
570
- country: billing.country ?? connInfo?.country ?? "",
571
- postcode: billing.postcode ?? connInfo?.postcode ?? ""
572
- };
573
- const response = await context.wordpress.woocommerce.store.cart.updateCustomer({
574
- billing_address: {
575
- ...address,
576
- email: billing.email ?? ""
577
- },
578
- ...input.shipping && { shipping_address: {
579
- ...address,
580
- first_name: input.shipping.firstName ?? "",
581
- last_name: input.shipping.lastName ?? "",
582
- address_1: input.shipping.address1 ?? "",
583
- address_2: input.shipping.address2 ?? "",
584
- company: input.shipping.company ?? "",
585
- phone: input.shipping.phone ?? "",
586
- city: input.shipping.city ?? "",
587
- state: input.shipping.state ?? connInfo?.state ?? "",
588
- country: input.shipping.country ?? connInfo?.country ?? "",
589
- postcode: input.shipping.postcode ?? connInfo?.postcode ?? ""
590
- } }
591
- }, { headers: context.sessionHeaders });
1223
+ const response = await context.wordpress.woocommerce.store.cart.updateCustomer(serializeCartUpdateInput(input), { headers: context.sessionHeaders });
592
1224
  if (response.error) switch (response.error.code) {
1225
+ case "rest_invalid_param":
1226
+ case "woocommerce_rest_invalid_address":
1227
+ case "woocommerce_rest_invalid_address_country":
1228
+ case "woocommerce_rest_invalid_email_address":
1229
+ case "woocommerce_rest_missing_email_address": throw errors.CART_ADDRESS_INVALID({ message: response.error.message });
593
1230
  default:
594
1231
  context.logger.error("Update cart customer unhandled error", response.error, { code: response.error.code });
595
1232
  throw errors.INTERNAL_SERVER_ERROR();
@@ -603,7 +1240,7 @@ const CART_PROCEDURES = {
603
1240
  body: SelectCartShippingRateInput,
604
1241
  output: Cart,
605
1242
  errors: SELECT_SHIPPING_RATE_ERROR_MAP,
606
- middlewares: [sessionMiddleware()]
1243
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
607
1244
  }, async ({ context, input: { body }, errors }) => {
608
1245
  const response = await context.wordpress.woocommerce.store.cart.selectShippingRate({
609
1246
  rate_id: body.rateId,
@@ -626,12 +1263,12 @@ const CART_PROCEDURES = {
626
1263
  body: AddCartItemInput,
627
1264
  output: Cart,
628
1265
  errors: ADD_CART_ITEM_ERROR_MAP,
629
- middlewares: [sessionMiddleware()]
1266
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
630
1267
  }, async ({ context, input: { body: input }, errors }) => {
631
1268
  const response = await context.wordpress.woocommerce.store.cart.addItem({
632
- id: input.productId,
1269
+ id: input.variationId ?? input.productId,
633
1270
  quantity: input.quantity,
634
- variation: input.variations ?? []
1271
+ variation: input.selectedAttributes ?? []
635
1272
  }, { headers: context.sessionHeaders });
636
1273
  if (response.error) switch (response.error.code) {
637
1274
  case "woocommerce_rest_product_out_of_stock": throw errors.CART_ITEM_OUT_OF_STOCK({ message: response.error.message });
@@ -659,7 +1296,7 @@ const CART_PROCEDURES = {
659
1296
  body: UpdateCartItemInput.pick({ quantity: true }),
660
1297
  output: Cart,
661
1298
  errors: UPDATE_CART_ITEM_ERROR_MAP,
662
- middlewares: [sessionMiddleware()]
1299
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
663
1300
  }, async ({ context, input: { params, body }, errors }) => {
664
1301
  const response = await context.wordpress.woocommerce.store.cart.updateItem({
665
1302
  key: params.key,
@@ -679,12 +1316,12 @@ const CART_PROCEDURES = {
679
1316
  }),
680
1317
  remove: createProcedure({
681
1318
  scope: "api",
682
- method: "PATCH",
1319
+ method: "DELETE",
683
1320
  path: "/cart/items/{key}",
684
1321
  params: RemoveCartItemInput.pick({ key: true }),
685
1322
  output: Cart,
686
1323
  errors: REMOVE_CART_ITEM_ERROR_MAP,
687
- middlewares: [sessionMiddleware()]
1324
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
688
1325
  }, async ({ context, input: { params }, errors }) => {
689
1326
  const response = await context.wordpress.woocommerce.store.cart.removeItem({ key: params.key }, { headers: context.sessionHeaders });
690
1327
  if (response.error) switch (response.error.code) {
@@ -704,7 +1341,7 @@ const CART_PROCEDURES = {
704
1341
  body: ApplyCouponInput,
705
1342
  output: Cart,
706
1343
  errors: APPLY_COUPON_ERROR_MAP,
707
- middlewares: [sessionMiddleware()]
1344
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
708
1345
  }, async ({ context, input: { body }, errors }) => {
709
1346
  const response = await context.wordpress.woocommerce.store.cart.applyCoupon({ code: body.code }, { headers: context.sessionHeaders });
710
1347
  if (response.error) switch (response.error.code) {
@@ -718,12 +1355,12 @@ const CART_PROCEDURES = {
718
1355
  }),
719
1356
  remove: createProcedure({
720
1357
  scope: "api",
721
- method: "POST",
1358
+ method: "DELETE",
722
1359
  path: "/cart/coupons/{code}",
723
1360
  params: RemoveCouponInput.pick({ code: true }),
724
1361
  output: Cart,
725
1362
  errors: REMOVE_COUPON_ERROR_MAP,
726
- middlewares: [sessionMiddleware()]
1363
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
727
1364
  }, async ({ context, input, errors }) => {
728
1365
  const response = await context.wordpress.woocommerce.store.cart.removeCoupon({ code: input.params.code }, { headers: context.sessionHeaders });
729
1366
  if (response.error) switch (response.error.code) {
@@ -741,6 +1378,8 @@ const CART_PROCEDURES = {
741
1378
 
742
1379
  //#endregion
743
1380
  //#region src/checkout/error.ts
1381
+ const CheckoutValidationData = z$2.object({ fields: z$2.record(z$2.string(), z$2.string()) });
1382
+ const CheckoutConflictData = z$2.object({ cart: Cart.nullable() });
744
1383
  const GET_CHECKOUT_ERROR_MAP = defineErrorMap({ CHECKOUT_ORDER_NOT_FOUND: {
745
1384
  status: 404,
746
1385
  message: "No checkout order found."
@@ -780,7 +1419,20 @@ const CONFIRM_CHECKOUT_ERROR_MAP = defineErrorMap({
780
1419
  },
781
1420
  CHECKOUT_VALIDATION_FAILED: {
782
1421
  status: 400,
783
- message: "Checkout validation failed."
1422
+ message: "Checkout validation failed.",
1423
+ data: CheckoutValidationData
1424
+ },
1425
+ CHECKOUT_ACCOUNT_CREATION_FAILED: {
1426
+ status: 400,
1427
+ message: "The customer account could not be created."
1428
+ },
1429
+ CHECKOUT_PAYMENT_RESULT_INVALID: {
1430
+ status: 500,
1431
+ message: "The payment gateway returned an invalid result."
1432
+ },
1433
+ CHECKOUT_ORDER_CREATION_FAILED: {
1434
+ status: 500,
1435
+ message: "The checkout order could not be created."
784
1436
  },
785
1437
  CHECKOUT_GUEST_DISABLED: {
786
1438
  status: 403,
@@ -792,31 +1444,38 @@ const CONFIRM_CHECKOUT_ERROR_MAP = defineErrorMap({
792
1444
  },
793
1445
  CHECKOUT_CART_EMPTY: {
794
1446
  status: 409,
795
- message: "The cart is empty."
1447
+ message: "The cart is empty.",
1448
+ data: CheckoutConflictData
796
1449
  },
797
1450
  CHECKOUT_CART_INVALID: {
798
1451
  status: 409,
799
- message: "An item in the cart is no longer valid."
1452
+ message: "An item in the cart is no longer valid.",
1453
+ data: CheckoutConflictData
800
1454
  },
801
1455
  CHECKOUT_COUPONS_REMOVED: {
802
1456
  status: 409,
803
- message: "One or more coupons were removed from the cart."
1457
+ message: "One or more coupons were removed from the cart.",
1458
+ data: CheckoutConflictData
804
1459
  },
805
1460
  CHECKOUT_COUPON_RESERVATION_FAILED: {
806
1461
  status: 409,
807
- message: "A coupon could not be reserved for this order."
1462
+ message: "A coupon could not be reserved for this order.",
1463
+ data: CheckoutConflictData
808
1464
  },
809
1465
  CHECKOUT_PRODUCT_INSUFFICIENT_STOCK: {
810
1466
  status: 409,
811
- message: "Not enough stock for one or more items in the cart."
1467
+ message: "Not enough stock for one or more items in the cart.",
1468
+ data: CheckoutConflictData
812
1469
  },
813
1470
  CHECKOUT_PRODUCT_NOT_PURCHASABLE: {
814
1471
  status: 409,
815
- message: "An item in the cart is no longer purchasable."
1472
+ message: "An item in the cart is no longer purchasable.",
1473
+ data: CheckoutConflictData
816
1474
  },
817
1475
  CHECKOUT_PRODUCT_OUT_OF_STOCK: {
818
1476
  status: 409,
819
- message: "An item in the cart is out of stock."
1477
+ message: "An item in the cart is out of stock.",
1478
+ data: CheckoutConflictData
820
1479
  }
821
1480
  });
822
1481
  const RETRY_CHECKOUT_ERROR_MAP = defineErrorMap({
@@ -836,6 +1495,15 @@ const RETRY_CHECKOUT_ERROR_MAP = defineErrorMap({
836
1495
  status: 400,
837
1496
  message: "A payment method is required."
838
1497
  },
1498
+ CHECKOUT_VALIDATION_FAILED: {
1499
+ status: 400,
1500
+ message: "Checkout validation failed.",
1501
+ data: CheckoutValidationData
1502
+ },
1503
+ CHECKOUT_PAYMENT_RESULT_INVALID: {
1504
+ status: 500,
1505
+ message: "The payment gateway returned an invalid result."
1506
+ },
839
1507
  CHECKOUT_ORDER_FORBIDDEN: {
840
1508
  status: 403,
841
1509
  message: "You are not allowed to pay for this order."
@@ -858,191 +1526,145 @@ const UPDATE_CHECKOUT_ERROR_MAP = defineErrorMap({
858
1526
  status: 400,
859
1527
  message: "The selected payment method is not available."
860
1528
  },
1529
+ CHECKOUT_VALIDATION_FAILED: {
1530
+ status: 400,
1531
+ message: "Checkout validation failed.",
1532
+ data: CheckoutValidationData
1533
+ },
861
1534
  CHECKOUT_ORDER_NOT_FOUND: {
862
1535
  status: 404,
863
1536
  message: "No checkout order found."
864
1537
  },
865
1538
  CHECKOUT_CART_EMPTY: {
866
1539
  status: 409,
867
- message: "The cart is empty."
1540
+ message: "The cart is empty.",
1541
+ data: CheckoutConflictData
868
1542
  },
869
1543
  CHECKOUT_CART_INVALID: {
870
1544
  status: 409,
871
- message: "An item in the cart is no longer valid."
1545
+ message: "An item in the cart is no longer valid.",
1546
+ data: CheckoutConflictData
872
1547
  },
873
1548
  CHECKOUT_PRODUCT_INSUFFICIENT_STOCK: {
874
1549
  status: 409,
875
- message: "Not enough stock for one or more items in the cart."
1550
+ message: "Not enough stock for one or more items in the cart.",
1551
+ data: CheckoutConflictData
876
1552
  },
877
1553
  CHECKOUT_PRODUCT_NOT_PURCHASABLE: {
878
1554
  status: 409,
879
- message: "An item in the cart is no longer purchasable."
1555
+ message: "An item in the cart is no longer purchasable.",
1556
+ data: CheckoutConflictData
880
1557
  },
881
1558
  CHECKOUT_PRODUCT_OUT_OF_STOCK: {
882
1559
  status: 409,
883
- message: "An item in the cart is out of stock."
1560
+ message: "An item in the cart is out of stock.",
1561
+ data: CheckoutConflictData
884
1562
  }
885
1563
  });
886
1564
 
887
1565
  //#endregion
888
1566
  //#region src/checkout/schema.ts
889
1567
  const CheckoutAdditionalFields = z$2.record(z$2.string(), z$2.union([z$2.string(), z$2.boolean()]));
1568
+ const CheckoutExtensions = z$2.record(z$2.string(), z$2.unknown());
890
1569
  const CheckoutPaymentData = z$2.array(z$2.object({
891
1570
  key: z$2.string(),
892
1571
  value: z$2.union([z$2.string(), z$2.boolean()])
893
1572
  }));
1573
+ const CheckoutPaymentResult = z$2.object({
1574
+ status: z$2.string(),
1575
+ details: z$2.array(z$2.object({
1576
+ key: z$2.string(),
1577
+ value: z$2.string()
1578
+ })),
1579
+ redirectUrl: z$2.string().nullable()
1580
+ });
894
1581
  const Checkout = z$2.object({
895
- cart: Cart.nullable(),
896
- billingAddress: BillingAddress,
897
- shippingAddress: ShippingAddress,
898
- paymentMethod: z$2.string(),
1582
+ orderId: z$2.number().nullable(),
1583
+ orderNumber: z$2.string().nullable(),
1584
+ orderKey: z$2.string().nullable(),
1585
+ status: z$2.string(),
1586
+ customerId: z$2.number().nullable(),
899
1587
  customerNote: z$2.string(),
1588
+ billingAddress: CartBillingAddress,
1589
+ shippingAddress: CartShippingAddress,
1590
+ paymentMethod: z$2.string().nullable(),
1591
+ paymentResult: CheckoutPaymentResult.nullable(),
900
1592
  additionalFields: CheckoutAdditionalFields,
901
- paymentResult: z$2.object({
902
- status: z$2.enum([
903
- "success",
904
- "pending",
905
- "failure",
906
- "error"
907
- ]),
908
- data: z$2.array(z$2.object({
909
- key: z$2.string(),
910
- value: z$2.string()
911
- })).optional(),
912
- redirectUrl: z$2.string()
913
- }).nullable()
1593
+ cart: Cart.nullable(),
1594
+ extensions: CheckoutExtensions
914
1595
  });
915
1596
  const UpdateCheckoutInput = z$2.object({
916
1597
  paymentMethod: z$2.string().optional(),
917
1598
  customerNote: z$2.string().optional(),
918
1599
  recalculateTotals: z$2.boolean().optional(),
919
- additionalFields: CheckoutAdditionalFields.optional()
1600
+ additionalFields: CheckoutAdditionalFields.optional(),
1601
+ extensions: CheckoutExtensions.optional()
920
1602
  });
921
1603
  const ConfirmCheckoutInput = z$2.object({
1604
+ billingAddress: CartBillingAddress,
1605
+ shippingAddress: CartShippingAddress.optional(),
1606
+ paymentMethod: z$2.string(),
1607
+ customerNote: z$2.string().optional(),
1608
+ createAccount: z$2.boolean().optional(),
922
1609
  customerPassword: z$2.string().optional(),
923
- paymentData: CheckoutPaymentData.optional()
1610
+ paymentData: CheckoutPaymentData.optional(),
1611
+ additionalFields: CheckoutAdditionalFields.optional(),
1612
+ extensions: CheckoutExtensions.optional()
924
1613
  });
925
1614
  const RetryCheckoutInput = z$2.object({
926
1615
  key: z$2.string(),
927
1616
  orderId: NumberLike,
928
1617
  paymentMethod: z$2.string(),
929
1618
  billingEmail: z$2.email().optional(),
930
- billingAddress: BillingAddress,
1619
+ billingAddress: CartBillingAddress,
931
1620
  paymentData: CheckoutPaymentData.optional(),
932
- shippingAddress: ShippingAddress.optional()
1621
+ shippingAddress: CartShippingAddress.optional(),
1622
+ customerNote: z$2.string().optional(),
1623
+ additionalFields: CheckoutAdditionalFields.optional(),
1624
+ extensions: CheckoutExtensions.optional()
933
1625
  });
934
1626
 
935
1627
  //#endregion
936
1628
  //#region src/checkout/utils.ts
937
- /**
938
- * A payment method the caller named, passed to WooCommerce as one.
939
- *
940
- * WooCommerce builds this argument's enum from `get_payment_gateway_ids()`, so the contract
941
- * enumerates the gateways enabled on the WordPress the client was generated against. That is true of
942
- * that install and says nothing about the one a build actually talks to, and WooCommerce agrees: its
943
- * own comment on the enum is that further validation happens during the request.
944
- *
945
- * So the enum is documentation here rather than a constraint, and the gateway a shopper chose is
946
- * sent whatever it is. WooCommerce rejects an unknown one with
947
- * `woocommerce_rest_checkout_payment_method_disabled`, which both callers already map.
948
- */
1629
+ function assertNoMissing$1() {}
1630
+ assertNoMissing$1();
1631
+ assertNoMissing$1();
1632
+ assertNoMissing$1();
1633
+ assertNoMissing$1();
949
1634
  function gateway(method) {
950
1635
  return method;
951
1636
  }
952
- /**
953
- * A Kizlo address in the shape WooCommerce registered its argument in.
954
- *
955
- * Kizlo's addresses are camelCase and WooCommerce's are snake_case, and until the contract described
956
- * the route nothing said so: the retry call passed a Kizlo address straight through as
957
- * `billing_address`, where every key missed and WooCommerce kept the address already on the order.
958
- *
959
- * Every field is required there, so an address given in part is sent filled out. An address that was
960
- * not given at all is a different thing and never reaches here: WooCommerce reads an absent
961
- * `shipping_address` as "use the billing address", and a blank one as an address, so a caller who
962
- * omitted it must leave the argument off the call rather than send this filled with empty strings.
963
- */
964
- function serializeAddress(address) {
965
- return {
966
- first_name: address.firstName,
967
- last_name: address.lastName,
968
- address_1: address.address1,
969
- address_2: address.address2 ?? "",
970
- company: address.company ?? "",
971
- city: address.city,
972
- state: address.state,
973
- country: address.country,
974
- postcode: address.postcode,
975
- phone: address.phone
976
- };
977
- }
978
- /** The same, plus the email WooCommerce carries on the billing address alone. */
979
- function serializeBillingAddress(address) {
980
- return {
981
- ...serializeAddress(address),
982
- email: address.email
983
- };
984
- }
985
1637
  function deserializeCheckout(data) {
1638
+ const { extensions } = deserializeExtensions(data.extensions);
1639
+ const paymentResult = data.payment_result;
986
1640
  return {
987
- cart: data.__experimentalCart ? deserializeCart(data.__experimentalCart) : null,
988
- shippingAddress: {
989
- address1: data.shipping_address.address_1,
990
- address2: data.shipping_address.address_2,
991
- city: data.shipping_address.city,
992
- company: data.shipping_address.company,
993
- country: data.shipping_address.country,
994
- firstName: data.shipping_address.first_name,
995
- lastName: data.shipping_address.last_name,
996
- phone: data.shipping_address.phone,
997
- postcode: data.shipping_address.postcode,
998
- state: data.shipping_address.state
999
- },
1000
- billingAddress: {
1001
- address1: data.billing_address.address_1,
1002
- address2: data.billing_address.address_2,
1003
- city: data.billing_address.city,
1004
- company: data.billing_address.company,
1005
- country: data.billing_address.country,
1006
- email: data.billing_address.email,
1007
- firstName: data.billing_address.first_name,
1008
- lastName: data.billing_address.last_name,
1009
- phone: data.billing_address.phone,
1010
- postcode: data.billing_address.postcode,
1011
- state: data.billing_address.state
1012
- },
1013
- additionalFields: additionalFields(data.additional_fields),
1641
+ orderId: data.order_id === 0 ? null : data.order_id,
1642
+ orderNumber: data.order_number === "" || data.order_number === "0" ? null : data.order_number,
1643
+ orderKey: data.order_key === "" ? null : data.order_key,
1644
+ status: data.status,
1645
+ customerId: data.customer_id === 0 ? null : data.customer_id,
1014
1646
  customerNote: data.customer_note,
1015
- paymentMethod: data.payment_method,
1016
- paymentResult: data.payment_result?.payment_status ? {
1017
- status: paymentStatus(data.payment_result.payment_status),
1018
- redirectUrl: data.payment_result.redirect_url,
1019
- data: data.payment_result.payment_details
1020
- } : null
1647
+ billingAddress: deserializeCartBillingAddress(data.billing_address),
1648
+ shippingAddress: deserializeCartShippingAddress(data.shipping_address),
1649
+ paymentMethod: data.payment_method === "" ? null : data.payment_method,
1650
+ paymentResult: paymentResult?.payment_status === void 0 || paymentResult.payment_status === "" ? null : {
1651
+ status: paymentResult.payment_status,
1652
+ details: paymentResult.payment_details,
1653
+ redirectUrl: paymentResult.redirect_url === "" ? null : paymentResult.redirect_url
1654
+ },
1655
+ additionalFields: checkoutAdditionalFields(data.additional_fields),
1656
+ cart: data.__experimentalCart ? deserializeCart(data.__experimentalCart) : null,
1657
+ extensions
1021
1658
  };
1022
1659
  }
1023
- /**
1024
- * WooCommerce registers `additional_fields` as a bare object, so the contract describes it as one
1025
- * with undescribed contents: what a site's checkout fields are is a per-site question that no schema
1026
- * can answer ahead of time. The values are scalars in practice, and anything else is dropped rather
1027
- * than passed on to fail the procedure's own output validation with a less useful message.
1028
- */
1029
- function additionalFields(fields) {
1030
- const kept = {};
1031
- for (const [key, value] of Object.entries(fields ?? {})) if (typeof value === "string" || typeof value === "boolean") kept[key] = value;
1032
- return kept;
1660
+ function serializeCheckoutShippingAddress(address) {
1661
+ return serializeCartShippingAddress(address);
1033
1662
  }
1034
- /**
1035
- * WooCommerce names the four statuses in the field's description and declares the field a plain
1036
- * string, so the contract cannot narrow it. `error` is the safe reading of anything unrecognized: a
1037
- * gateway that answered with something new has not taken payment as far as this checkout knows.
1038
- */
1039
- function paymentStatus(status) {
1040
- switch (status) {
1041
- case "success":
1042
- case "pending":
1043
- case "failure": return status;
1044
- default: return "error";
1045
- }
1663
+ function serializeCheckoutBillingAddress(address) {
1664
+ return serializeCartBillingAddress(address);
1665
+ }
1666
+ function checkoutAdditionalFields(fields) {
1667
+ return Object.fromEntries(Object.entries(fields ?? {}).filter((entry) => typeof entry[1] === "string" || typeof entry[1] === "boolean"));
1046
1668
  }
1047
1669
 
1048
1670
  //#endregion
@@ -1054,14 +1676,13 @@ const CHECKOUT_PROCEDURES = {
1054
1676
  path: "/checkout",
1055
1677
  output: Checkout,
1056
1678
  errors: GET_CHECKOUT_ERROR_MAP,
1057
- middlewares: [sessionMiddleware()]
1679
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
1058
1680
  }, async ({ context, errors }) => {
1059
1681
  const response = await context.wordpress.woocommerce.store.checkout.get({}, { headers: context.sessionHeaders });
1060
- if (response.error) switch (response.error.code) {
1061
- case "woocommerce_rest_checkout_missing_order": throw errors.CHECKOUT_ORDER_NOT_FOUND({ message: response.error.message });
1062
- default:
1063
- context.logger.error("Get checkout unhandled error", response.error, { code: response.error.code });
1064
- throw errors.INTERNAL_SERVER_ERROR();
1682
+ if (response.error) {
1683
+ if (response.error.code === "woocommerce_rest_checkout_missing_order") throw errors.CHECKOUT_ORDER_NOT_FOUND({ message: response.error.message });
1684
+ context.logger.error("Get checkout unhandled error", response.error, { code: response.error.code });
1685
+ throw errors.INTERNAL_SERVER_ERROR();
1065
1686
  }
1066
1687
  return deserializeCheckout(response.data);
1067
1688
  }),
@@ -1072,26 +1693,49 @@ const CHECKOUT_PROCEDURES = {
1072
1693
  body: UpdateCheckoutInput,
1073
1694
  output: Checkout,
1074
1695
  errors: UPDATE_CHECKOUT_ERROR_MAP,
1075
- middlewares: [sessionMiddleware()]
1696
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
1076
1697
  }, async ({ context, input, errors }) => {
1077
1698
  const response = await context.wordpress.woocommerce.store.checkout.update({
1078
1699
  order_notes: input.body.customerNote,
1079
1700
  payment_method: gateway(input.body.paymentMethod),
1080
1701
  additional_fields: input.body.additionalFields,
1702
+ extensions: input.body.extensions,
1081
1703
  __experimental_calc_totals: input.body.recalculateTotals
1082
1704
  }, { headers: context.sessionHeaders });
1083
- if (response.error) switch (response.error.code) {
1084
- case "woocommerce_rest_cart_coupon_error": throw errors.CHECKOUT_COUPON_INVALID({ message: response.error.message });
1085
- case "woocommerce_rest_checkout_payment_method_disabled": throw errors.CHECKOUT_PAYMENT_METHOD_DISABLED({ message: response.error.message });
1086
- case "woocommerce_rest_checkout_missing_order": throw errors.CHECKOUT_ORDER_NOT_FOUND({ message: response.error.message });
1087
- case "woocommerce_rest_cart_empty": throw errors.CHECKOUT_CART_EMPTY({ message: response.error.message });
1088
- case "woocommerce_rest_cart_item_error": throw errors.CHECKOUT_CART_INVALID({ message: response.error.message });
1089
- case "woocommerce_rest_product_partially_out_of_stock": throw errors.CHECKOUT_PRODUCT_INSUFFICIENT_STOCK({ message: response.error.message });
1090
- case "woocommerce_rest_product_not_purchasable": throw errors.CHECKOUT_PRODUCT_NOT_PURCHASABLE({ message: response.error.message });
1091
- case "woocommerce_rest_product_out_of_stock": throw errors.CHECKOUT_PRODUCT_OUT_OF_STOCK({ message: response.error.message });
1092
- default:
1093
- context.logger.error("Update checkout unhandled error", response.error, { code: response.error.code });
1094
- throw errors.INTERNAL_SERVER_ERROR();
1705
+ if (response.error) {
1706
+ const conflict = conflictData(response.error.data);
1707
+ switch (response.error.code) {
1708
+ case "rest_invalid_param": throw errors.CHECKOUT_VALIDATION_FAILED({
1709
+ message: response.error.message,
1710
+ data: { fields: validationFields(response.error.data) }
1711
+ });
1712
+ case "woocommerce_rest_cart_coupon_error": throw errors.CHECKOUT_COUPON_INVALID({ message: response.error.message });
1713
+ case "woocommerce_rest_checkout_payment_method_disabled": throw errors.CHECKOUT_PAYMENT_METHOD_DISABLED({ message: response.error.message });
1714
+ case "woocommerce_rest_checkout_missing_order": throw errors.CHECKOUT_ORDER_NOT_FOUND({ message: response.error.message });
1715
+ case "woocommerce_rest_cart_empty": throw errors.CHECKOUT_CART_EMPTY({
1716
+ message: response.error.message,
1717
+ data: conflict
1718
+ });
1719
+ case "woocommerce_rest_cart_item_error": throw errors.CHECKOUT_CART_INVALID({
1720
+ message: response.error.message,
1721
+ data: conflict
1722
+ });
1723
+ case "woocommerce_rest_product_partially_out_of_stock": throw errors.CHECKOUT_PRODUCT_INSUFFICIENT_STOCK({
1724
+ message: response.error.message,
1725
+ data: conflict
1726
+ });
1727
+ case "woocommerce_rest_product_not_purchasable": throw errors.CHECKOUT_PRODUCT_NOT_PURCHASABLE({
1728
+ message: response.error.message,
1729
+ data: conflict
1730
+ });
1731
+ case "woocommerce_rest_product_out_of_stock": throw errors.CHECKOUT_PRODUCT_OUT_OF_STOCK({
1732
+ message: response.error.message,
1733
+ data: conflict
1734
+ });
1735
+ default:
1736
+ context.logger.error("Update checkout unhandled error", response.error, { code: response.error.code });
1737
+ throw errors.INTERNAL_SERVER_ERROR();
1738
+ }
1095
1739
  }
1096
1740
  return deserializeCheckout(response.data);
1097
1741
  }),
@@ -1102,49 +1746,78 @@ const CHECKOUT_PROCEDURES = {
1102
1746
  body: ConfirmCheckoutInput,
1103
1747
  output: Checkout,
1104
1748
  errors: CONFIRM_CHECKOUT_ERROR_MAP,
1105
- middlewares: [sessionMiddleware()]
1749
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
1106
1750
  }, async ({ context, input, errors }) => {
1107
- const checkoutResponse = await context.wordpress.woocommerce.store.checkout.get({}, { headers: context.sessionHeaders });
1108
- if (checkoutResponse.error) switch (checkoutResponse.error.code) {
1109
- case "woocommerce_rest_checkout_missing_order": throw errors.CHECKOUT_ORDER_NOT_FOUND({ message: checkoutResponse.error.message });
1110
- default:
1111
- context.logger.error("Get checkout for confirm unhandled error", checkoutResponse.error, { code: checkoutResponse.error.code });
1112
- throw errors.INTERNAL_SERVER_ERROR();
1113
- }
1114
- const confirmResponse = await context.wordpress.woocommerce.store.checkout.process({
1115
- payment_data: input.body.paymentData,
1751
+ const response = await context.wordpress.woocommerce.store.checkout.process({
1752
+ billing_address: serializeCheckoutBillingAddress(input.body.billingAddress),
1753
+ shipping_address: input.body.shippingAddress ? serializeCheckoutShippingAddress(input.body.shippingAddress) : void 0,
1754
+ payment_method: gateway(input.body.paymentMethod),
1755
+ customer_note: input.body.customerNote,
1756
+ create_account: input.body.createAccount ?? false,
1116
1757
  customer_password: input.body.customerPassword,
1117
- customer_note: checkoutResponse.data.customer_note,
1118
- payment_method: checkoutResponse.data.payment_method,
1119
- create_account: !!input.body.customerPassword?.length,
1120
- billing_address: checkoutResponse.data.billing_address,
1121
- shipping_address: checkoutResponse.data.shipping_address,
1122
- additional_fields: checkoutResponse.data.additional_fields
1758
+ payment_data: input.body.paymentData,
1759
+ additional_fields: input.body.additionalFields,
1760
+ extensions: input.body.extensions
1123
1761
  }, { headers: context.sessionHeaders });
1124
- if (confirmResponse.error) switch (confirmResponse.error.code) {
1125
- case "woocommerce_rest_invalid_address": throw errors.CHECKOUT_ADDRESS_INVALID({ message: confirmResponse.error.message });
1126
- case "woocommerce_rest_invalid_address_country": throw errors.CHECKOUT_ADDRESS_COUNTRY_INVALID({ message: confirmResponse.error.message });
1127
- case "woocommerce_rest_cart_coupon_error": throw errors.CHECKOUT_COUPON_INVALID({ message: confirmResponse.error.message });
1128
- case "woocommerce_rest_invalid_email_address": throw errors.CHECKOUT_EMAIL_INVALID({ message: confirmResponse.error.message });
1129
- case "woocommerce_rest_missing_email_address": throw errors.CHECKOUT_EMAIL_MISSING({ message: confirmResponse.error.message });
1130
- case "woocommerce_rest_checkout_process_payment_error": throw errors.CHECKOUT_PAYMENT_FAILED({ message: confirmResponse.error.message });
1131
- case "woocommerce_rest_checkout_payment_method_disabled": throw errors.CHECKOUT_PAYMENT_METHOD_DISABLED({ message: confirmResponse.error.message });
1132
- case "woocommerce_rest_checkout_missing_payment_method": throw errors.CHECKOUT_PAYMENT_METHOD_MISSING({ message: confirmResponse.error.message });
1133
- case "woocommerce_rest_checkout_custom_validation_error": throw errors.CHECKOUT_VALIDATION_FAILED({ message: confirmResponse.error.message });
1134
- case "woocommerce_rest_guest_checkout_disabled": throw errors.CHECKOUT_GUEST_DISABLED({ message: confirmResponse.error.message });
1135
- case "woocommerce_rest_checkout_missing_order": throw errors.CHECKOUT_ORDER_NOT_FOUND({ message: confirmResponse.error.message });
1136
- case "woocommerce_rest_cart_empty": throw errors.CHECKOUT_CART_EMPTY({ message: confirmResponse.error.message });
1137
- case "woocommerce_rest_cart_item_error": throw errors.CHECKOUT_CART_INVALID({ message: confirmResponse.error.message });
1138
- case "removed_coupons": throw errors.CHECKOUT_COUPONS_REMOVED({ message: confirmResponse.error.message });
1139
- case "woocommerce_rest_coupon_reserve_failed": throw errors.CHECKOUT_COUPON_RESERVATION_FAILED({ message: confirmResponse.error.message });
1140
- case "woocommerce_rest_product_partially_out_of_stock": throw errors.CHECKOUT_PRODUCT_INSUFFICIENT_STOCK({ message: confirmResponse.error.message });
1141
- case "woocommerce_rest_product_not_purchasable": throw errors.CHECKOUT_PRODUCT_NOT_PURCHASABLE({ message: confirmResponse.error.message });
1142
- case "woocommerce_rest_product_out_of_stock": throw errors.CHECKOUT_PRODUCT_OUT_OF_STOCK({ message: confirmResponse.error.message });
1143
- default:
1144
- context.logger.error("Confirm checkout unhandled error", confirmResponse.error, { code: confirmResponse.error.code });
1145
- throw errors.INTERNAL_SERVER_ERROR();
1762
+ if (response.error) {
1763
+ const conflict = conflictData(response.error.data);
1764
+ switch (response.error.code) {
1765
+ case "rest_invalid_param": throw errors.CHECKOUT_VALIDATION_FAILED({
1766
+ message: response.error.message,
1767
+ data: { fields: validationFields(response.error.data) }
1768
+ });
1769
+ case "woocommerce_rest_invalid_address": throw errors.CHECKOUT_ADDRESS_INVALID({ message: response.error.message });
1770
+ case "woocommerce_rest_invalid_address_country": throw errors.CHECKOUT_ADDRESS_COUNTRY_INVALID({ message: response.error.message });
1771
+ case "woocommerce_rest_cart_coupon_error": throw errors.CHECKOUT_COUPON_INVALID({ message: response.error.message });
1772
+ case "woocommerce_rest_invalid_email_address": throw errors.CHECKOUT_EMAIL_INVALID({ message: response.error.message });
1773
+ case "woocommerce_rest_missing_email_address": throw errors.CHECKOUT_EMAIL_MISSING({ message: response.error.message });
1774
+ case "woocommerce_rest_checkout_process_payment_error": throw errors.CHECKOUT_PAYMENT_FAILED({ message: response.error.message });
1775
+ case "woocommerce_rest_checkout_payment_method_disabled": throw errors.CHECKOUT_PAYMENT_METHOD_DISABLED({ message: response.error.message });
1776
+ case "woocommerce_rest_checkout_missing_payment_method": throw errors.CHECKOUT_PAYMENT_METHOD_MISSING({ message: response.error.message });
1777
+ case "woocommerce_rest_checkout_custom_validation_error": throw errors.CHECKOUT_VALIDATION_FAILED({
1778
+ message: response.error.message,
1779
+ data: { fields: {} }
1780
+ });
1781
+ case "woocommerce_rest_checkout_invalid_payment_result": throw errors.CHECKOUT_PAYMENT_RESULT_INVALID({ message: response.error.message });
1782
+ case "woocommerce_rest_guest_checkout_disabled": throw errors.CHECKOUT_GUEST_DISABLED({ message: response.error.message });
1783
+ case "woocommerce_rest_checkout_missing_order":
1784
+ if (response.status >= 500) throw errors.CHECKOUT_ORDER_CREATION_FAILED({ message: response.error.message });
1785
+ throw errors.CHECKOUT_ORDER_NOT_FOUND({ message: response.error.message });
1786
+ case "woocommerce_rest_cart_empty": throw errors.CHECKOUT_CART_EMPTY({
1787
+ message: response.error.message,
1788
+ data: conflict
1789
+ });
1790
+ case "woocommerce_rest_cart_item_error": throw errors.CHECKOUT_CART_INVALID({
1791
+ message: response.error.message,
1792
+ data: conflict
1793
+ });
1794
+ case "removed_coupons": throw errors.CHECKOUT_COUPONS_REMOVED({
1795
+ message: response.error.message,
1796
+ data: conflict
1797
+ });
1798
+ case "woocommerce_rest_coupon_reserve_failed": throw errors.CHECKOUT_COUPON_RESERVATION_FAILED({
1799
+ message: response.error.message,
1800
+ data: conflict
1801
+ });
1802
+ case "woocommerce_rest_product_partially_out_of_stock": throw errors.CHECKOUT_PRODUCT_INSUFFICIENT_STOCK({
1803
+ message: response.error.message,
1804
+ data: conflict
1805
+ });
1806
+ case "woocommerce_rest_product_not_purchasable": throw errors.CHECKOUT_PRODUCT_NOT_PURCHASABLE({
1807
+ message: response.error.message,
1808
+ data: conflict
1809
+ });
1810
+ case "woocommerce_rest_product_out_of_stock": throw errors.CHECKOUT_PRODUCT_OUT_OF_STOCK({
1811
+ message: response.error.message,
1812
+ data: conflict
1813
+ });
1814
+ default:
1815
+ if (input.body.createAccount && response.status === 400) throw errors.CHECKOUT_ACCOUNT_CREATION_FAILED({ message: response.error.message });
1816
+ context.logger.error("Confirm checkout unhandled error", response.error, { code: response.error.code });
1817
+ throw errors.INTERNAL_SERVER_ERROR();
1818
+ }
1146
1819
  }
1147
- return deserializeCheckout(confirmResponse.data);
1820
+ return deserializeCheckout(response.data);
1148
1821
  }),
1149
1822
  retry: createProcedure({
1150
1823
  scope: "api",
@@ -1154,7 +1827,7 @@ const CHECKOUT_PROCEDURES = {
1154
1827
  body: RetryCheckoutInput.omit({ orderId: true }),
1155
1828
  output: Checkout,
1156
1829
  errors: RETRY_CHECKOUT_ERROR_MAP,
1157
- middlewares: [sessionMiddleware()]
1830
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
1158
1831
  }, async ({ context, input, errors }) => {
1159
1832
  const response = await context.wordpress.woocommerce.store.checkout.processOrder({
1160
1833
  key: input.body.key,
@@ -1162,14 +1835,22 @@ const CHECKOUT_PROCEDURES = {
1162
1835
  payment_data: input.body.paymentData,
1163
1836
  billing_email: input.body.billingEmail,
1164
1837
  payment_method: gateway(input.body.paymentMethod),
1165
- billing_address: serializeBillingAddress(input.body.billingAddress),
1166
- shipping_address: input.body.shippingAddress ? serializeAddress(input.body.shippingAddress) : void 0
1838
+ billing_address: serializeCheckoutBillingAddress(input.body.billingAddress),
1839
+ shipping_address: input.body.shippingAddress ? serializeCheckoutShippingAddress(input.body.shippingAddress) : void 0,
1840
+ customer_note: input.body.customerNote,
1841
+ additional_fields: input.body.additionalFields,
1842
+ extensions: input.body.extensions
1167
1843
  }, { headers: context.sessionHeaders });
1168
1844
  if (response.error) switch (response.error.code) {
1845
+ case "rest_invalid_param": throw errors.CHECKOUT_VALIDATION_FAILED({
1846
+ message: response.error.message,
1847
+ data: { fields: validationFields(response.error.data) }
1848
+ });
1169
1849
  case "woocommerce_rest_invalid_billing_email": throw errors.CHECKOUT_EMAIL_INVALID({ message: response.error.message });
1170
1850
  case "woocommerce_rest_checkout_process_payment_error": throw errors.CHECKOUT_PAYMENT_FAILED({ message: response.error.message });
1171
1851
  case "woocommerce_rest_checkout_payment_method_disabled": throw errors.CHECKOUT_PAYMENT_METHOD_DISABLED({ message: response.error.message });
1172
1852
  case "woocommerce_rest_checkout_missing_payment_method": throw errors.CHECKOUT_PAYMENT_METHOD_MISSING({ message: response.error.message });
1853
+ case "woocommerce_rest_checkout_invalid_payment_result": throw errors.CHECKOUT_PAYMENT_RESULT_INVALID({ message: response.error.message });
1173
1854
  case "woocommerce_rest_invalid_user": throw errors.CHECKOUT_ORDER_FORBIDDEN({ message: response.error.message });
1174
1855
  case "woocommerce_rest_invalid_order": throw errors.CHECKOUT_ORDER_NOT_FOUND({ message: response.error.message });
1175
1856
  case "invalid_order_update_status": throw errors.CHECKOUT_ORDER_STATUS_INVALID({ message: response.error.message });
@@ -1183,6 +1864,62 @@ const CHECKOUT_PROCEDURES = {
1183
1864
  return deserializeCheckout(response.data);
1184
1865
  })
1185
1866
  };
1867
+ function validationFields(data) {
1868
+ if (!isRecord(data)) return {};
1869
+ const fields = {};
1870
+ for (const source of [data.params, data.details]) {
1871
+ if (!isRecord(source)) continue;
1872
+ for (const [name, value] of Object.entries(source)) if (typeof value === "string") fields[name] = value;
1873
+ else if (isRecord(value) && typeof value.message === "string") fields[name] = value.message;
1874
+ }
1875
+ return fields;
1876
+ }
1877
+ function conflictData(data) {
1878
+ if (!isRecord(data) || !isRecord(data.cart)) return { cart: null };
1879
+ try {
1880
+ return { cart: deserializeCart(data.cart) };
1881
+ } catch {
1882
+ return { cart: null };
1883
+ }
1884
+ }
1885
+ function isRecord(value) {
1886
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1887
+ }
1888
+
1889
+ //#endregion
1890
+ //#region src/schema.ts
1891
+ const Totals = z$1.object({
1892
+ discountTotal: z$1.number(),
1893
+ discountTaxTotal: z$1.number(),
1894
+ shippingTotal: z$1.number(),
1895
+ shippingTaxTotal: z$1.number(),
1896
+ feeTotal: z$1.number(),
1897
+ feeTaxTotal: z$1.number(),
1898
+ taxTotal: z$1.number(),
1899
+ total: z$1.number()
1900
+ });
1901
+ const ItemTotals = z$1.object({
1902
+ unitPrice: z$1.number(),
1903
+ grossAmount: z$1.number(),
1904
+ discountAmount: z$1.number(),
1905
+ discountTaxAmount: z$1.number(),
1906
+ netAmount: z$1.number(),
1907
+ taxAmount: z$1.number(),
1908
+ total: z$1.number()
1909
+ });
1910
+ const ShippingAddress = z$1.object({
1911
+ firstName: z$1.string(),
1912
+ lastName: z$1.string(),
1913
+ phone: z$1.string(),
1914
+ company: z$1.string().optional(),
1915
+ address1: z$1.string(),
1916
+ address2: z$1.string().optional(),
1917
+ city: z$1.string(),
1918
+ postcode: z$1.string(),
1919
+ state: z$1.string(),
1920
+ country: z$1.string()
1921
+ });
1922
+ const BillingAddress = ShippingAddress.extend({ email: z$1.string() });
1186
1923
 
1187
1924
  //#endregion
1188
1925
  //#region src/customer/schema.ts
@@ -1222,593 +1959,312 @@ function deserializeCustomer(data) {
1222
1959
  },
1223
1960
  shipping: {
1224
1961
  firstName: data.shipping.first_name,
1225
- lastName: data.shipping.last_name,
1226
- address1: data.shipping.address_1,
1227
- city: data.shipping.city,
1228
- country: data.shipping.country,
1229
- phone: data.shipping.phone,
1230
- postcode: data.shipping.postcode,
1231
- state: data.shipping.state,
1232
- address2: data.shipping.address_2,
1233
- company: data.shipping.company
1234
- },
1235
- email: data.email,
1236
- firstName: data.first_name,
1237
- lastName: data.last_name,
1238
- isPayingCustomer: data.is_paying_customer,
1239
- meta: toPublicMetadata(data.meta_data),
1240
- registeredAt: timestampFromWpGmt(data.date_created_gmt) ?? 0,
1241
- role: data.role,
1242
- username: data.username
1243
- };
1244
- }
1245
-
1246
- //#endregion
1247
- //#region src/customer/index.ts
1248
- const CUSTOMER_PROCEDURES = { get: createProcedure({
1249
- scope: "api",
1250
- method: "GET",
1251
- path: "/customers",
1252
- output: Customer
1253
- }, async ({ context, errors }) => {
1254
- const auth = await context.getAuthUser();
1255
- if (!auth) throw errors.FORBIDDEN();
1256
- const response = await context.wordpress.woocommerce.customers.retrieve({ id: auth.id });
1257
- if (response.error) switch (response.error.code) {
1258
- case "wc_user_invalid_id": throw errors.NOT_FOUND();
1259
- case "woocommerce_rest_cannot_view": throw errors.FORBIDDEN();
1260
- default:
1261
- context.logger.error("Get customer unhandled error", response.error, {
1262
- userId: auth.id,
1263
- code: response.error.code
1264
- });
1265
- throw errors.INTERNAL_SERVER_ERROR();
1266
- }
1267
- return deserializeCustomer(response.data);
1268
- }) };
1269
-
1270
- //#endregion
1271
- //#region src/product/error.ts
1272
- const GET_PRODUCT_ERROR_MAP = defineErrorMap({ PRODUCT_NOT_FOUND: {
1273
- status: 404,
1274
- message: "Product not found."
1275
- } });
1276
- const LIST_PRODUCT_ERROR_MAP = defineErrorMap({});
1277
-
1278
- //#endregion
1279
- //#region src/product/schema.ts
1280
- const SWATCH_TYPES = [
1281
- "text",
1282
- "color",
1283
- "image"
1284
- ];
1285
- const SwatchType = z.enum(SWATCH_TYPES);
1286
- const PRODUCT_TYPES = [
1287
- "simple",
1288
- "grouped",
1289
- "external",
1290
- "variable",
1291
- "variation"
1292
- ];
1293
- const ProductType = z.enum(PRODUCT_TYPES);
1294
- const ProductTermSummary = z.object({
1295
- id: z.number(),
1296
- name: z.string(),
1297
- slug: z.string(),
1298
- url: z.string().nullable()
1299
- });
1300
- const ProductTagSummary = ProductTermSummary;
1301
- const ProductBrandSummary = ProductTermSummary;
1302
- const ProductCategorySummary = ProductTermSummary;
1303
- const ProductAttributeTermSummary = z.object({
1304
- id: z.number(),
1305
- name: z.string(),
1306
- slug: z.string(),
1307
- isDefault: z.boolean()
1308
- });
1309
- const ProductAttributeSummary = z.object({
1310
- id: z.number(),
1311
- name: z.string(),
1312
- taxonomy: z.string().nullable(),
1313
- hasVariations: z.boolean(),
1314
- terms: z.array(ProductAttributeTermSummary)
1315
- });
1316
- const ProductVariationAttributeSummary = z.object({
1317
- name: z.string(),
1318
- value: z.string().nullable()
1319
- });
1320
- const ProductVariationSummary = z.object({
1321
- id: z.number(),
1322
- attributes: z.array(ProductVariationAttributeSummary)
1323
- });
1324
- const ProductPriceRange = z.object({
1325
- minAmount: z.number(),
1326
- maxAmount: z.number()
1327
- });
1328
- const ProductPrices = z.object({
1329
- price: z.number(),
1330
- regularPrice: z.number(),
1331
- salePrice: z.number().nullable(),
1332
- priceRange: ProductPriceRange.nullable()
1333
- });
1334
- const ProductStockAvailability = z.object({
1335
- text: z.string(),
1336
- class: z.string()
1337
- });
1338
- const ProductDimensions = z.object({
1339
- length: z.string(),
1340
- width: z.string(),
1341
- height: z.string()
1342
- });
1343
- const ProductAddToCart = z.object({
1344
- text: z.string(),
1345
- description: z.string(),
1346
- singleText: z.string(),
1347
- minimum: z.number(),
1348
- maximum: z.number(),
1349
- multipleOf: z.number()
1962
+ lastName: data.shipping.last_name,
1963
+ address1: data.shipping.address_1,
1964
+ city: data.shipping.city,
1965
+ country: data.shipping.country,
1966
+ phone: data.shipping.phone,
1967
+ postcode: data.shipping.postcode,
1968
+ state: data.shipping.state,
1969
+ address2: data.shipping.address_2,
1970
+ company: data.shipping.company
1971
+ },
1972
+ email: data.email,
1973
+ firstName: data.first_name,
1974
+ lastName: data.last_name,
1975
+ isPayingCustomer: data.is_paying_customer,
1976
+ meta: toPublicMetadata(data.meta_data),
1977
+ registeredAt: timestampFromWpGmt(data.date_created_gmt) ?? 0,
1978
+ role: data.role,
1979
+ username: data.username
1980
+ };
1981
+ }
1982
+
1983
+ //#endregion
1984
+ //#region src/customer/index.ts
1985
+ const CUSTOMER_PROCEDURES = { get: createProcedure({
1986
+ scope: "api",
1987
+ method: "GET",
1988
+ path: "/customers",
1989
+ output: Customer
1990
+ }, async ({ context, errors }) => {
1991
+ const session = await context.getSession();
1992
+ if (!session) throw errors.FORBIDDEN();
1993
+ const response = await context.wordpress.woocommerce.customers.list({
1994
+ email: session.email,
1995
+ role: "all"
1996
+ });
1997
+ if (response.error) switch (response.error.code) {
1998
+ case "woocommerce_rest_cannot_view": throw errors.FORBIDDEN();
1999
+ default:
2000
+ context.logger.error("Get customer unhandled error", response.error, {
2001
+ email: session.email,
2002
+ code: response.error.code
2003
+ });
2004
+ throw errors.INTERNAL_SERVER_ERROR();
2005
+ }
2006
+ const customer = response.data[0];
2007
+ if (!customer) throw errors.NOT_FOUND();
2008
+ return deserializeCustomer(customer);
2009
+ }) };
2010
+
2011
+ //#endregion
2012
+ //#region src/order/error.ts
2013
+ const GET_ORDER_ERROR_MAP = defineErrorMap({
2014
+ ORDER_NOT_FOUND: {
2015
+ status: 404,
2016
+ message: "Order not found."
2017
+ },
2018
+ ORDER_FORBIDDEN: {
2019
+ status: 403,
2020
+ message: "You are not allowed to view this order."
2021
+ }
1350
2022
  });
1351
- const ProductExtensions = z.record(z.string(), z.unknown());
1352
- const ProductSummary = z.object({
1353
- id: z.number(),
1354
- name: z.string(),
2023
+
2024
+ //#endregion
2025
+ //#region src/order/schema.ts
2026
+ const OrderItemProduct = z.object({
2027
+ sku: z.string().nullable(),
1355
2028
  slug: z.string(),
1356
- parentId: z.number().nullable(),
1357
- type: z.string(),
1358
- variationDescription: z.string(),
1359
2029
  url: z.string().nullable(),
1360
- sku: z.string().nullable(),
1361
2030
  shortDescription: z.string(),
1362
2031
  description: z.string(),
1363
- isPasswordProtected: z.boolean(),
1364
- isOnSale: z.boolean(),
1365
- prices: ProductPrices,
1366
- currencyFormat: CurrencyFormat,
1367
- priceHtml: z.string(),
1368
- averageRating: z.number(),
1369
- reviewCount: z.number(),
1370
2032
  images: z.array(MediaImage),
1371
- categories: z.array(ProductCategorySummary),
1372
- tags: z.array(ProductTagSummary),
1373
- brands: z.array(ProductBrandSummary),
1374
- attributes: z.array(ProductAttributeSummary),
1375
- variations: z.array(ProductVariationSummary),
1376
- groupedProductIds: z.array(z.number()),
1377
- hasOptions: z.boolean(),
1378
- isPurchasable: z.boolean(),
1379
- isInStock: z.boolean(),
1380
- isOnBackorder: z.boolean(),
1381
- stockAvailability: ProductStockAvailability,
1382
- lowStockRemaining: z.number().nullable(),
1383
- isSoldIndividually: z.boolean(),
1384
- addToCart: ProductAddToCart,
1385
- extensions: ProductExtensions
1386
- });
1387
- const ProductRecommendations = z.object({
1388
- upsells: z.array(ProductSummary),
1389
- crossSells: z.array(ProductSummary),
1390
- related: z.array(ProductSummary)
1391
- });
1392
- const ProductCustomFieldsSchema = customFieldsSchema();
1393
- const Product = ProductSummary.extend({
1394
- weight: z.string(),
1395
- dimensions: ProductDimensions,
1396
- formattedWeight: z.string(),
1397
- formattedDimensions: z.string(),
1398
- stockQuantity: z.number().nullable(),
1399
- saleStartsAt: z.number().nullable(),
1400
- saleEndsAt: z.number().nullable(),
1401
- seo: Seo.nullable(),
1402
- custom: ProductCustomFieldsSchema,
1403
- recommendations: ProductRecommendations.nullable()
1404
- });
1405
- const ProductList = z.object({
1406
- items: z.array(Product),
1407
- meta: ListMetadata
1408
- });
1409
- const RetrieveProductInput = z.object({
1410
- identifier: IdentifierInput,
1411
- previewToken: z.string().optional(),
1412
- recommendations: BooleanLike.optional()
1413
- });
1414
- const PRODUCTS_ORDER_BYS = [
1415
- "date",
1416
- "modified",
1417
- "id",
1418
- "include",
1419
- "title",
1420
- "slug",
1421
- "price",
1422
- "popularity",
1423
- "rating",
1424
- "menu_order",
1425
- "comment_count"
1426
- ];
1427
- const ProductOrderBy = z.enum(PRODUCTS_ORDER_BYS);
1428
- const PRODUCT_DATE_COLUMNS = [
1429
- "date",
1430
- "date_gmt",
1431
- "modified",
1432
- "modified_gmt"
1433
- ];
1434
- const ProductDateColumn = z.enum(PRODUCT_DATE_COLUMNS);
1435
- const PRODUCT_TAXONOMY_OPERATORS = [
1436
- "in",
1437
- "not_in",
1438
- "and"
1439
- ];
1440
- const ProductTaxonomyOperator = z.enum(PRODUCT_TAXONOMY_OPERATORS);
1441
- const PRODUCT_ATTRIBUTE_RELATIONS = ["in", "and"];
1442
- const ProductAttributeRelation = z.enum(PRODUCT_ATTRIBUTE_RELATIONS);
1443
- const PRODUCT_STOCK_STATUSES = [
1444
- "instock",
1445
- "outofstock",
1446
- "onbackorder"
1447
- ];
1448
- const ProductStockStatus = z.enum(PRODUCT_STOCK_STATUSES);
1449
- const PRODUCT_CATALOG_VISIBILITIES = [
1450
- "any",
1451
- "visible",
1452
- "catalog",
1453
- "search",
1454
- "hidden"
1455
- ];
1456
- const ProductCatalogVisibility = z.enum(PRODUCT_CATALOG_VISIBILITIES);
1457
- const PRODUCT_RATINGS = [
1458
- 1,
1459
- 2,
1460
- 3,
1461
- 4,
1462
- 5
1463
- ];
1464
- const ProductRating = z.union([
1465
- z.literal(1),
1466
- z.literal(2),
1467
- z.literal(3),
1468
- z.literal(4),
1469
- z.literal(5)
1470
- ]);
1471
- const ProductRatingInput = NumberLike.pipe(ProductRating);
1472
- const ProductTermIdentifier = z.union([NumberLike, z.string()]);
1473
- const ProductTaxonomyName = z.string().min(1).regex(/^[a-z0-9_-]+$/).refine((name) => !name.endsWith("_operator"));
1474
- const ProductAttributeFilter = z.object({
1475
- taxonomy: z.string(),
1476
- slug: arrayable(z.string()).optional(),
1477
- termId: arrayable(NumberLike).optional(),
1478
- operator: ProductTaxonomyOperator.optional()
1479
- });
1480
- const ProductTaxonomyFilter = z.object({
1481
- taxonomy: ProductTaxonomyName,
1482
- termIds: arrayable(NumberLike).optional(),
1483
- slugs: arrayable(z.string()).optional(),
1484
- operator: ProductTaxonomyOperator.optional()
1485
- }).refine((filter) => filter.termIds === void 0 !== (filter.slugs === void 0), { message: "Provide either termIds or slugs." });
1486
- const ListProductInput = z.object({
1487
- page: NumberLike.optional(),
1488
- perPage: NumberLike.optional(),
1489
- search: z.string().optional(),
1490
- recommendations: BooleanLike.optional(),
1491
- slug: arrayable(z.string()).optional(),
1492
- after: z.string().optional(),
1493
- before: z.string().optional(),
1494
- dateColumn: ProductDateColumn.optional(),
1495
- exclude: arrayable(NumberLike).optional(),
1496
- include: arrayable(NumberLike).optional(),
1497
- offset: NumberLike.optional(),
1498
- order: z.enum(["asc", "desc"]).optional(),
1499
- orderBy: ProductOrderBy.optional(),
1500
- parent: arrayable(NumberLike).optional(),
1501
- parentExclude: arrayable(NumberLike).optional(),
1502
- type: z.string().optional(),
1503
- sku: arrayable(z.string()).optional(),
1504
- featured: BooleanLike.optional(),
1505
- category: arrayable(ProductTermIdentifier).optional(),
1506
- categoryOperator: ProductTaxonomyOperator.optional(),
1507
- brand: arrayable(ProductTermIdentifier).optional(),
1508
- brandOperator: ProductTaxonomyOperator.optional(),
1509
- tag: arrayable(ProductTermIdentifier).optional(),
1510
- tagOperator: ProductTaxonomyOperator.optional(),
1511
- onSale: BooleanLike.optional(),
1512
- minPrice: NumberLike.optional(),
1513
- maxPrice: NumberLike.optional(),
1514
- stockStatus: arrayable(ProductStockStatus).optional(),
1515
- attributes: z.array(ProductAttributeFilter).optional(),
1516
- attributeRelation: ProductAttributeRelation.optional(),
1517
- catalogVisibility: ProductCatalogVisibility.optional(),
1518
- rating: arrayable(ProductRatingInput).optional(),
1519
- related: NumberLike.optional(),
1520
- taxonomies: z.array(ProductTaxonomyFilter).optional()
1521
- });
1522
- const ProductFiltersPriceRange = z.object({
1523
- minPrice: z.number(),
1524
- maxPrice: z.number()
1525
- });
1526
- const ProductFiltersStockStatus = z.object({
1527
- count: z.number(),
1528
- status: ProductStockStatus
2033
+ prices: ProductPrices,
2034
+ custom: ProductCustomFieldsSchema
1529
2035
  });
1530
- const ProductFiltersRatingCount = z.object({
1531
- count: z.number(),
1532
- rating: ProductRating
2036
+ const OrderItem = z.object({
2037
+ id: z.number(),
2038
+ productId: z.number().nullable(),
2039
+ variationId: z.number().nullable(),
2040
+ name: z.string(),
2041
+ quantity: z.number(),
2042
+ selectedAttributes: z.array(CartSelectedAttribute),
2043
+ itemData: z.array(CartItemData),
2044
+ totals: CartItemTotals,
2045
+ product: OrderItemProduct.nullable(),
2046
+ extensions: z.record(z.string(), z.unknown())
1533
2047
  });
1534
- const ProductFiltersTerm = z.object({
2048
+ const OrderFee = z.object({
1535
2049
  id: z.number(),
1536
- parentId: z.number().nullable(),
1537
2050
  name: z.string(),
1538
- slug: z.string(),
1539
- taxonomy: z.string(),
1540
- description: z.string(),
1541
- count: z.number()
2051
+ totals: z.object({
2052
+ total: z.number(),
2053
+ tax: z.number()
2054
+ })
1542
2055
  });
1543
- const ProductFiltersTaxonomyTerm = ProductFiltersTerm.extend({ image: MediaImage.nullable() });
1544
- const ProductFiltersAttributeTerm = ProductFiltersTerm.extend({
1545
- type: SwatchType,
1546
- swatch: z.string().nullable()
2056
+ const OrderTotals = z.object({
2057
+ subtotal: z.number(),
2058
+ itemsTotal: z.number(),
2059
+ itemsTaxTotal: z.number(),
2060
+ feesTotal: z.number(),
2061
+ feesTaxTotal: z.number(),
2062
+ discountTotal: z.number(),
2063
+ discountTaxTotal: z.number(),
2064
+ shippingTotal: z.number().nullable(),
2065
+ shippingTaxTotal: z.number().nullable(),
2066
+ taxTotal: z.number(),
2067
+ refundTotal: z.number(),
2068
+ total: z.number(),
2069
+ taxLines: z.array(CartTaxLine)
1547
2070
  });
1548
- const ProductFilters = z.object({
1549
- priceRange: ProductFiltersPriceRange,
1550
- ratingCounts: z.array(ProductFiltersRatingCount),
1551
- stockStatuses: z.array(ProductFiltersStockStatus),
1552
- attributeTerms: z.array(ProductFiltersAttributeTerm),
1553
- taxonomyTerms: z.array(ProductFiltersTaxonomyTerm),
2071
+ const Order = z.object({
2072
+ id: z.number(),
2073
+ status: z.string(),
2074
+ items: z.array(OrderItem),
2075
+ coupons: z.array(CartCoupon),
2076
+ fees: z.array(OrderFee),
2077
+ billingAddress: CartBillingAddress,
2078
+ shippingAddress: CartShippingAddress,
2079
+ needsPayment: z.boolean(),
2080
+ needsShipping: z.boolean(),
2081
+ paymentRequirements: z.array(z.string()),
2082
+ errors: z.array(CartError),
2083
+ totals: OrderTotals,
1554
2084
  currencyFormat: CurrencyFormat
1555
2085
  });
1556
- const ProductAttributeCount = z.object({
1557
- taxonomy: z.string(),
1558
- operator: z.enum(["or", "and"]).optional()
1559
- });
1560
- const RetrieveProductFiltersInput = ListProductInput.omit({ recommendations: true }).extend({
1561
- ratingCounts: BooleanLike.optional(),
1562
- stockStatusCounts: BooleanLike.optional(),
1563
- taxonomyCounts: z.array(z.string()).optional(),
1564
- attributeCounts: z.array(ProductAttributeCount).optional()
2086
+ const GetOrderInput = z.object({
2087
+ orderId: NumberLike,
2088
+ key: z.string().optional(),
2089
+ billingEmail: z.email().optional()
1565
2090
  });
1566
2091
 
1567
2092
  //#endregion
1568
- //#region src/product/utils.ts
1569
- function assertNoMissingStoreProductInputs() {}
1570
- assertNoMissingStoreProductInputs();
1571
- function deserializeProduct(data) {
1572
- return deserializeStoreProduct(data.kizlo.store_product, null);
1573
- }
1574
- function deserializeStoreProduct(data, recommendations) {
1575
- const summary = deserializeProductSummary(data);
1576
- const { kizlo } = deserializeExtensions(data.extensions);
1577
- return {
1578
- ...summary,
1579
- weight: data.weight,
1580
- dimensions: data.dimensions,
1581
- formattedWeight: data.formatted_weight,
1582
- formattedDimensions: data.formatted_dimensions,
1583
- stockQuantity: nullableNumber(kizlo.stock),
1584
- saleStartsAt: timestampFromIso(typeof kizlo.on_sale_from === "string" ? kizlo.on_sale_from : null),
1585
- saleEndsAt: timestampFromIso(typeof kizlo.on_sale_to === "string" ? kizlo.on_sale_to : null),
1586
- seo: isRecord(kizlo.seo) ? deserializeSeo(kizlo.seo) : null,
1587
- custom: productCustomFields(kizlo.custom),
1588
- recommendations
1589
- };
1590
- }
1591
- function deserializeProductSummary(data) {
1592
- const { extensions, kizlo } = deserializeExtensions(data.extensions);
1593
- const termUrls = deserializeTermUrls(kizlo.term_urls);
2093
+ //#region src/order/utils.ts
2094
+ function assertNoMissing() {}
2095
+ assertNoMissing();
2096
+ assertNoMissing();
2097
+ assertNoMissing();
2098
+ assertNoMissing();
2099
+ assertNoMissing();
2100
+ assertNoMissing();
2101
+ assertNoMissing();
2102
+ assertNoMissing();
2103
+ assertNoMissing();
2104
+ assertNoMissing();
2105
+ assertNoMissing();
2106
+ assertNoMissing();
2107
+ assertNoMissing();
2108
+ assertNoMissing();
2109
+ assertNoMissing();
2110
+ assertNoMissing();
2111
+ function deserializeOrder(data) {
1594
2112
  return {
1595
2113
  id: data.id,
1596
- name: data.name,
1597
- slug: data.slug,
1598
- parentId: data.parent === 0 ? null : data.parent,
1599
- type: data.type,
1600
- variationDescription: data.variation,
1601
- url: typeof kizlo.url === "string" ? kizlo.url : null,
1602
- sku: data.sku === "" ? null : data.sku,
1603
- shortDescription: data.short_description,
1604
- description: data.description,
1605
- isPasswordProtected: data.is_password_protected,
1606
- isOnSale: data.on_sale,
1607
- prices: {
1608
- price: Number(data.prices.price),
1609
- regularPrice: Number(data.prices.regular_price),
1610
- salePrice: data.on_sale ? Number(data.prices.sale_price) : null,
1611
- priceRange: data.prices.price_range ? {
1612
- minAmount: Number(data.prices.price_range.min_amount),
1613
- maxAmount: Number(data.prices.price_range.max_amount)
1614
- } : null
1615
- },
1616
- currencyFormat: deserializeCurrencyFormat(data.prices),
1617
- priceHtml: data.price_html,
1618
- averageRating: Number(data.average_rating),
1619
- reviewCount: data.review_count,
1620
- images: data.images.map((image) => ({
1621
- type: "image",
1622
- id: image.id,
1623
- src: image.src,
1624
- srcset: image.srcset,
1625
- name: image.name,
1626
- alt: image.alt
2114
+ status: data.status,
2115
+ items: data.items.map(deserializeOrderItem),
2116
+ coupons: data.coupons.map((coupon) => ({
2117
+ code: coupon.code,
2118
+ discountType: coupon.discount_type,
2119
+ totals: {
2120
+ discount: Number(coupon.totals.total_discount),
2121
+ discountTax: Number(coupon.totals.total_discount_tax)
2122
+ }
1627
2123
  })),
1628
- categories: data.categories.map((term) => deserializeTermRef(term, "product_cat", termUrls)),
1629
- tags: data.tags.map((term) => deserializeTermRef(term, "product_tag", termUrls)),
1630
- brands: data.brands.map((term) => deserializeTermRef(term, "product_brand", termUrls)),
1631
- attributes: data.attributes.map((attribute) => ({
1632
- id: attribute.id,
1633
- name: attribute.name,
1634
- taxonomy: attribute.taxonomy ?? null,
1635
- hasVariations: attribute.has_variations,
1636
- terms: attribute.terms.map((term) => ({
1637
- id: term.id,
1638
- name: term.name,
1639
- slug: term.slug,
1640
- isDefault: term.default
1641
- }))
2124
+ fees: data.fees.map((fee) => ({
2125
+ id: fee.key,
2126
+ name: fee.name,
2127
+ totals: {
2128
+ total: Number(fee.totals.total),
2129
+ tax: Number(fee.totals.total_tax)
2130
+ }
1642
2131
  })),
1643
- variations: data.variations.map((variation) => ({
1644
- id: variation.id,
1645
- attributes: variation.attributes.map((attribute) => ({
1646
- name: attribute.name,
1647
- value: attribute.value ?? null
2132
+ billingAddress: deserializeCartBillingAddress(data.billing_address),
2133
+ shippingAddress: deserializeCartShippingAddress(data.shipping_address),
2134
+ needsPayment: data.needs_payment,
2135
+ needsShipping: data.needs_shipping,
2136
+ paymentRequirements: data.payment_requirements,
2137
+ errors: data.errors,
2138
+ totals: {
2139
+ subtotal: Number(data.totals.subtotal),
2140
+ itemsTotal: Number(data.totals.total_items),
2141
+ itemsTaxTotal: Number(data.totals.total_items_tax),
2142
+ feesTotal: Number(data.totals.total_fees),
2143
+ feesTaxTotal: Number(data.totals.total_fees_tax),
2144
+ discountTotal: Number(data.totals.total_discount),
2145
+ discountTaxTotal: Number(data.totals.total_discount_tax),
2146
+ shippingTotal: nullableMoney(data.totals.total_shipping),
2147
+ shippingTaxTotal: nullableMoney(data.totals.total_shipping_tax),
2148
+ taxTotal: Number(data.totals.total_tax),
2149
+ refundTotal: Number(data.totals.total_refund),
2150
+ total: Number(data.totals.total_price),
2151
+ taxLines: data.totals.tax_lines.map((line) => ({
2152
+ name: line.name,
2153
+ price: Number(line.price),
2154
+ rate: line.rate
1648
2155
  }))
1649
- })),
1650
- groupedProductIds: data.grouped_products,
1651
- hasOptions: data.has_options,
1652
- isPurchasable: data.is_purchasable,
1653
- isInStock: data.is_in_stock,
1654
- isOnBackorder: data.is_on_backorder,
1655
- stockAvailability: data.stock_availability,
1656
- lowStockRemaining: data.low_stock_remaining,
1657
- isSoldIndividually: data.sold_individually,
1658
- addToCart: {
1659
- text: data.add_to_cart.text,
1660
- description: data.add_to_cart.description,
1661
- singleText: data.add_to_cart.single_text,
1662
- minimum: data.add_to_cart.minimum,
1663
- maximum: data.add_to_cart.maximum,
1664
- multipleOf: data.add_to_cart.multiple_of
1665
2156
  },
1666
- extensions
1667
- };
1668
- }
1669
- function deserializeProductRecommendations(data) {
1670
- return {
1671
- upsells: deserializeEmbeddedProducts(data._embedded?.upsells),
1672
- crossSells: deserializeEmbeddedProducts(data._embedded?.cross_sells),
1673
- related: deserializeEmbeddedProducts(data._embedded?.related)
2157
+ currencyFormat: deserializeCurrencyFormat(data.totals)
1674
2158
  };
1675
2159
  }
1676
- function productCustomFields(value) {
1677
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
1678
- }
1679
- function deserializeProductFilters(data) {
1680
- if (!data.price_range) return null;
1681
- const maxPrice = +data.price_range.max_price;
1682
- const minPrice = +data.price_range.min_price;
2160
+ function deserializeOrderItem(item) {
2161
+ const { extensions, kizlo } = deserializeExtensions(item.extensions);
2162
+ const productId = nullableId(kizlo.product_id);
2163
+ const variationId = nullableId(kizlo.variation_id);
1683
2164
  return {
1684
- ratingCounts: (data.rating_counts ?? []).flatMap((entry) => PRODUCT_RATINGS.includes(entry.rating) ? [{
1685
- count: entry.count,
1686
- rating: entry.rating
1687
- }] : []),
1688
- stockStatuses: (data.stock_status_counts ?? []).flatMap((entry) => {
1689
- const status = stockStatus(entry.status);
1690
- return status ? [{
1691
- count: entry.count,
1692
- status
1693
- }] : [];
1694
- }),
1695
- taxonomyTerms: data.kizlo.taxonomy_counts.map((item) => ({
1696
- id: item.id,
1697
- name: item.name,
1698
- count: item.count,
1699
- description: item.description,
1700
- parentId: item.parent,
1701
- slug: item.slug,
1702
- taxonomy: item.taxonomy,
1703
- image: item.image
2165
+ id: item.id,
2166
+ productId,
2167
+ variationId,
2168
+ name: item.name,
2169
+ quantity: item.quantity,
2170
+ selectedAttributes: item.variation.map((attribute) => ({
2171
+ name: attribute.attribute,
2172
+ attribute: attribute.raw_attribute,
2173
+ value: attribute.value
1704
2174
  })),
1705
- attributeTerms: data.kizlo.attribute_counts.map((item) => ({
1706
- id: item.id,
1707
- name: item.name,
1708
- count: item.count,
1709
- description: item.description,
1710
- parentId: item.parent,
1711
- slug: item.slug,
1712
- swatch: item.swatch,
1713
- type: item.swatch_type,
1714
- taxonomy: item.taxonomy
2175
+ itemData: item.item_data.map((entry) => ({
2176
+ name: entry.name,
2177
+ value: entry.value,
2178
+ display: entry.display
1715
2179
  })),
1716
- priceRange: {
1717
- maxPrice,
1718
- minPrice
2180
+ totals: {
2181
+ subtotal: Number(item.totals.line_subtotal),
2182
+ subtotalTax: Number(item.totals.line_subtotal_tax),
2183
+ total: Number(item.totals.line_total),
2184
+ totalTax: Number(item.totals.line_total_tax)
1719
2185
  },
1720
- currencyFormat: deserializeCurrencyFormat(data.price_range)
1721
- };
1722
- }
1723
- function serializeProductListInput(data) {
1724
- const searchParams = {
1725
- after: data?.after,
1726
- attribute_relation: data?.attributeRelation,
1727
- before: data?.before,
1728
- brand: commaSeparated(data?.brand),
1729
- brand_operator: data?.brandOperator,
1730
- catalog_visibility: data?.catalogVisibility,
1731
- category: commaSeparated(data?.category),
1732
- category_operator: data?.categoryOperator,
1733
- date_column: data?.dateColumn,
1734
- featured: data?.featured,
1735
- max_price: data?.maxPrice === void 0 ? void 0 : String(data.maxPrice),
1736
- min_price: data?.minPrice === void 0 ? void 0 : String(data.minPrice),
1737
- on_sale: data?.onSale,
1738
- orderby: data?.orderBy,
1739
- parent: normalizeArrayableValue(data?.parent),
1740
- parent_exclude: normalizeArrayableValue(data?.parentExclude),
1741
- rating: normalizeArrayableValue(data?.rating),
1742
- sku: commaSeparated(data?.sku),
1743
- slug: commaSeparated(data?.slug),
1744
- stock_status: normalizeArrayableValue(data?.stockStatus),
1745
- tag: commaSeparated(data?.tag),
1746
- tag_operator: data?.tagOperator,
1747
- type: data?.type,
1748
- attributes: data?.attributes?.map((item) => ({
1749
- operator: item.operator,
1750
- attribute: item.taxonomy,
1751
- slug: normalizeArrayableValue(item.slug),
1752
- term_id: normalizeArrayableValue(item.termId)
1753
- })),
1754
- exclude: normalizeArrayableValue(data?.exclude),
1755
- include: normalizeArrayableValue(data?.include),
1756
- offset: data?.offset,
1757
- order: data?.order,
1758
- page: data?.page,
1759
- per_page: data?.perPage,
1760
- related: data?.related,
1761
- search: data?.search
1762
- };
1763
- for (const filter of data?.taxonomies ?? []) {
1764
- const key = `_unstable_tax_${filter.taxonomy}`;
1765
- searchParams[key] = commaSeparated(filter.termIds ?? filter.slugs);
1766
- if (filter.operator !== void 0) searchParams[`${key}_operator`] = filter.operator;
1767
- }
1768
- return searchParams;
1769
- }
1770
- function commaSeparated(value) {
1771
- if (value === void 0) return void 0;
1772
- return (Array.isArray(value) ? value : [value]).join(",");
1773
- }
1774
- function stockStatus(status) {
1775
- return PRODUCT_STOCK_STATUSES.includes(status) ? status : null;
1776
- }
1777
- function deserializeTermRef(term, taxonomy, urls) {
1778
- return {
1779
- id: term.id,
1780
- name: term.name,
1781
- slug: term.slug,
1782
- url: urls[`${taxonomy}:${term.id}`] ?? null
2186
+ product: kizlo.product_exists === true ? deserializeOrderItemProduct(item, kizlo) : null,
2187
+ extensions
1783
2188
  };
1784
2189
  }
1785
- function deserializeTermUrls(value) {
1786
- if (!Array.isArray(value)) return {};
1787
- return Object.fromEntries(value.flatMap((item) => {
1788
- if (!isRecord(item) || typeof item.id !== "number" || typeof item.taxonomy !== "string" || typeof item.url !== "string") return [];
1789
- return [[`${item.taxonomy}:${item.id}`, item.url]];
1790
- }));
1791
- }
1792
- function deserializeEmbeddedProducts(collections) {
1793
- return (collections ?? []).flat().map(deserializeProductSummary);
1794
- }
1795
- function deserializeExtensions(value) {
1796
- const { kizlo: rawKizlo,...extensions } = asRecord(value);
2190
+ function deserializeOrderItemProduct(item, kizlo) {
1797
2191
  return {
1798
- extensions,
1799
- kizlo: asRecord(rawKizlo)
2192
+ sku: item.sku === "" ? null : item.sku,
2193
+ slug: typeof kizlo.slug === "string" ? kizlo.slug : "",
2194
+ url: typeof kizlo.url === "string" ? kizlo.url : null,
2195
+ shortDescription: item.short_description,
2196
+ description: item.description,
2197
+ images: item.images.map((image) => ({
2198
+ type: "image",
2199
+ id: image.id,
2200
+ name: image.name,
2201
+ alt: image.alt,
2202
+ src: image.src,
2203
+ srcset: image.srcset
2204
+ })),
2205
+ prices: {
2206
+ price: Number(item.prices.price),
2207
+ regularPrice: Number(item.prices.regular_price),
2208
+ salePrice: item.prices.sale_price === "" || item.prices.sale_price === item.prices.regular_price ? null : Number(item.prices.sale_price),
2209
+ priceRange: item.prices.price_range ? {
2210
+ minAmount: Number(item.prices.price_range.min_amount),
2211
+ maxAmount: Number(item.prices.price_range.max_amount)
2212
+ } : null
2213
+ },
2214
+ custom: productCustomFields(kizlo.custom)
1800
2215
  };
1801
2216
  }
1802
- function asRecord(value) {
1803
- return isRecord(value) ? value : {};
1804
- }
1805
- function isRecord(value) {
1806
- return typeof value === "object" && value !== null && !Array.isArray(value);
2217
+ function nullableId(value) {
2218
+ return typeof value === "number" && value !== 0 ? value : null;
1807
2219
  }
1808
- function nullableNumber(value) {
1809
- return typeof value === "number" ? value : null;
2220
+ function nullableMoney(value) {
2221
+ return value === null ? null : Number(value);
1810
2222
  }
1811
2223
 
2224
+ //#endregion
2225
+ //#region src/order/index.ts
2226
+ const ORDER_PROCEDURES = { get: createProcedure({
2227
+ scope: "api",
2228
+ method: "GET",
2229
+ path: "/orders/{orderId}",
2230
+ params: GetOrderInput.pick({ orderId: true }),
2231
+ query: GetOrderInput.pick({
2232
+ key: true,
2233
+ billingEmail: true
2234
+ }).optional(),
2235
+ output: schemaType(Order),
2236
+ errors: GET_ORDER_ERROR_MAP,
2237
+ middlewares: [sessionMiddleware()]
2238
+ }, async ({ context, input, errors }) => {
2239
+ const response = await context.wordpress.woocommerce.store.orders.get({
2240
+ id: input.params.orderId,
2241
+ key: input.query?.key,
2242
+ billing_email: input.query?.billingEmail
2243
+ }, { headers: context.sessionHeaders });
2244
+ if (response.error) switch (response.error.code) {
2245
+ case "woocommerce_rest_invalid_order":
2246
+ if (response.status === 404) throw errors.ORDER_NOT_FOUND({ message: response.error.message });
2247
+ throw errors.ORDER_FORBIDDEN({ message: response.error.message });
2248
+ case "woocommerce_rest_invalid_billing_email":
2249
+ case "woocommerce_rest_invalid_user": throw errors.ORDER_FORBIDDEN({ message: response.error.message });
2250
+ default:
2251
+ context.logger.error("Get order unhandled error", response.error, {
2252
+ orderId: input.params.orderId,
2253
+ code: response.error.code
2254
+ });
2255
+ throw errors.INTERNAL_SERVER_ERROR();
2256
+ }
2257
+ return deserializeOrder(response.data);
2258
+ }) };
2259
+
2260
+ //#endregion
2261
+ //#region src/product/error.ts
2262
+ const GET_PRODUCT_ERROR_MAP = defineErrorMap({ PRODUCT_NOT_FOUND: {
2263
+ status: 404,
2264
+ message: "Product not found."
2265
+ } });
2266
+ const LIST_PRODUCT_ERROR_MAP = defineErrorMap({});
2267
+
1812
2268
  //#endregion
1813
2269
  //#region src/product/index.ts
1814
2270
  const PRODUCT_EMBEDS = "upsells,cross_sells,related";
@@ -1928,14 +2384,14 @@ function woocommerce() {
1928
2384
  requires: {
1929
2385
  plugins: [{
1930
2386
  name: "kizlo-woocommerce",
1931
- version: "0.3.0"
2387
+ version: "0.5.0"
1932
2388
  }],
1933
2389
  endpoints: [
1934
2390
  "woocommerce.customers",
1935
2391
  "woocommerce.products",
1936
- "woocommerce.kizlo.cart",
1937
2392
  "woocommerce.store.cart",
1938
2393
  "woocommerce.store.checkout",
2394
+ "woocommerce.store.orders",
1939
2395
  "woocommerce.store.products"
1940
2396
  ]
1941
2397
  },
@@ -1943,10 +2399,11 @@ function woocommerce() {
1943
2399
  cart: CART_PROCEDURES,
1944
2400
  products: PRODUCT_PROCEDURES,
1945
2401
  checkout: CHECKOUT_PROCEDURES,
1946
- customers: CUSTOMER_PROCEDURES
2402
+ customers: CUSTOMER_PROCEDURES,
2403
+ orders: ORDER_PROCEDURES
1947
2404
  }
1948
2405
  });
1949
2406
  }
1950
2407
 
1951
2408
  //#endregion
1952
- export { ListProductInput, PRODUCT_ATTRIBUTE_RELATIONS, PRODUCT_CATALOG_VISIBILITIES, PRODUCT_DATE_COLUMNS, PRODUCT_RATINGS, PRODUCT_STOCK_STATUSES, PRODUCT_TYPES, Product, ProductAddToCart, ProductAttributeCount, ProductAttributeFilter, ProductAttributeRelation, ProductAttributeSummary, ProductAttributeTermSummary, ProductBrandSummary, ProductCatalogVisibility, ProductCategorySummary, ProductCustomFieldsSchema, ProductDateColumn, ProductDimensions, ProductExtensions, ProductFilters, ProductFiltersAttributeTerm, ProductFiltersPriceRange, ProductFiltersRatingCount, ProductFiltersStockStatus, ProductFiltersTaxonomyTerm, ProductFiltersTerm, ProductList, ProductOrderBy, ProductPriceRange, ProductPrices, ProductRating, ProductRecommendations, ProductStockAvailability, ProductStockStatus, ProductSummary, ProductTagSummary, ProductTaxonomyFilter, ProductTaxonomyOperator, ProductTermSummary, ProductType, ProductVariationAttributeSummary, ProductVariationSummary, RetrieveProductFiltersInput, RetrieveProductInput, SWATCH_TYPES, SwatchType, woocommerce };
2409
+ export { AddCartItemInput, ApplyCouponInput, Cart, CartAdditionalFields, CartBillingAddress, CartCoupon, CartCouponTotals, CartError, CartFee, CartFeeTotals, CartItem, CartItemData, CartItemQuantityLimits, CartItemTotals, CartPaymentMethod, CartSelectedAttribute, CartShippingAddress, CartShippingDestination, CartShippingItem, CartShippingPackage, CartShippingRate, CartShippingRateMetadata, CartTaxLine, CartTotals, Checkout, CheckoutAdditionalFields, CheckoutExtensions, CheckoutPaymentData, CheckoutPaymentResult, ConfirmCheckoutInput, GetOrderInput, ListProductInput, Order, OrderFee, OrderItem, OrderItemProduct, OrderTotals, PRODUCT_ATTRIBUTE_RELATIONS, PRODUCT_CATALOG_VISIBILITIES, PRODUCT_DATE_COLUMNS, PRODUCT_RATINGS, PRODUCT_STOCK_STATUSES, PRODUCT_TYPES, Product, ProductAddToCart, ProductAttributeCount, ProductAttributeFilter, ProductAttributeRelation, ProductAttributeSummary, ProductAttributeTermSummary, ProductBrandSummary, ProductCatalogVisibility, ProductCategorySummary, ProductCustomFieldsSchema, ProductDateColumn, ProductDimensions, ProductExtensions, ProductFilters, ProductFiltersAttributeTerm, ProductFiltersPriceRange, ProductFiltersRatingCount, ProductFiltersStockStatus, ProductFiltersTaxonomyTerm, ProductFiltersTerm, ProductList, ProductOrderBy, ProductPriceRange, ProductPrices, ProductRating, ProductRecommendations, ProductStockAvailability, ProductStockStatus, ProductSummary, ProductTagSummary, ProductTaxonomyFilter, ProductTaxonomyOperator, ProductTermSummary, ProductType, ProductVariationAttributeSummary, ProductVariationSummary, RemoveCartItemInput, RemoveCouponInput, RetrieveProductFiltersInput, RetrieveProductInput, RetryCheckoutInput, SWATCH_TYPES, SelectCartShippingRateInput, SwatchType, UpdateCartInput, UpdateCartItemInput, UpdateCheckoutInput, woocommerce };