@kizlo/woocommerce 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { CurrencyFormat, IdentifierInput, KizloError, ListMetadata, Seo, createIntegration, createMiddleware, createProcedure, customFieldsSchema, defineErrorMap, deserializeCurrencyFormat, deserializeListMetadata, deserializeSeo } from "kizlo";
1
+ import { CurrencyFormat, IdentifierInput, KizloError, ListMetadata, Seo, createIntegration, createMiddleware, createProcedure, customFieldsSchema, defineErrorMap, deserializeCurrencyFormat, deserializeListMetadata, deserializeSeo, schemaType } from "kizlo";
2
2
  import { BooleanLike, MediaImage, Metadata, NumberLike, arrayable, normalizeArrayableValue, random, seconds, timestampFromIso, timestampFromWpGmt, timestampSec, toPublicMetadata, tryCatch } from "@kizlo/shared";
3
3
  import { SignJWT, jwtVerify } from "jose";
4
4
  import z$1, { z } from "zod/v4";
@@ -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,353 +194,987 @@ 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 }) => {
539
- const response = await context.wordpress.woocommerce.store.cart.get({}, { headers: context.sessionHeaders });
540
- if (response.error) switch (response.error.code) {
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 Cart = z$1.object({
631
+ items: z$1.array(CartItem),
632
+ itemCount: z$1.number(),
633
+ itemsWeight: z$1.number(),
634
+ billingAddress: CartBillingAddress,
635
+ shippingAddress: CartShippingAddress,
636
+ shippingPackages: z$1.array(CartShippingPackage),
637
+ coupons: z$1.array(CartCoupon),
638
+ fees: z$1.array(CartFee),
639
+ crossSells: z$1.array(ProductSummary),
640
+ needsPayment: z$1.boolean(),
641
+ needsShipping: z$1.boolean(),
642
+ hasCalculatedShipping: z$1.boolean(),
643
+ paymentMethods: z$1.array(z$1.string()),
644
+ paymentRequirements: z$1.array(z$1.string()),
645
+ errors: z$1.array(CartError),
646
+ totals: CartTotals,
647
+ currencyFormat: CurrencyFormat,
648
+ extensions: z$1.record(z$1.string(), z$1.unknown())
649
+ });
650
+ const AddCartItemInput = z$1.object({
651
+ productId: z$1.number(),
652
+ variationId: z$1.number().optional(),
653
+ quantity: z$1.number().optional(),
654
+ selectedAttributes: z$1.array(z$1.object({
655
+ attribute: z$1.string(),
656
+ value: z$1.string()
657
+ })).optional()
658
+ });
659
+ const UpdateCartItemInput = z$1.object({
660
+ key: z$1.string(),
661
+ quantity: z$1.number()
662
+ });
663
+ const RemoveCartItemInput = z$1.object({ key: z$1.string() });
664
+ const ApplyCouponInput = z$1.object({ code: z$1.string() });
665
+ const RemoveCouponInput = z$1.object({ code: z$1.string() });
666
+ const SelectCartShippingRateInput = z$1.object({
667
+ rateId: z$1.string(),
668
+ packageId: z$1.union([z$1.number(), z$1.string()]).nullable().optional()
669
+ });
670
+ const CartShippingAddressInput = z$1.object(CartAddressFields).partial();
671
+ const CartBillingAddressInput = z$1.object({
672
+ ...CartAddressFields,
673
+ email: z$1.string()
674
+ }).partial();
675
+ const UpdateCartInput = z$1.object({
676
+ shippingAddress: CartShippingAddressInput.optional(),
677
+ billingAddress: CartBillingAddressInput.optional()
678
+ });
679
+
680
+ //#endregion
681
+ //#region src/product/utils.ts
682
+ function assertNoMissingStoreProductInputs() {}
683
+ assertNoMissingStoreProductInputs();
684
+ function deserializeProduct(data) {
685
+ return deserializeStoreProduct(data.kizlo.store_product, null);
686
+ }
687
+ function deserializeStoreProduct(data, recommendations) {
688
+ const summary = deserializeProductSummary(data);
689
+ const { kizlo } = deserializeExtensions(data.extensions);
690
+ return {
691
+ ...summary,
692
+ weight: data.weight,
693
+ dimensions: data.dimensions,
694
+ formattedWeight: data.formatted_weight,
695
+ formattedDimensions: data.formatted_dimensions,
696
+ stockQuantity: nullableNumber(kizlo.stock),
697
+ saleStartsAt: timestampFromIso(typeof kizlo.on_sale_from === "string" ? kizlo.on_sale_from : null),
698
+ saleEndsAt: timestampFromIso(typeof kizlo.on_sale_to === "string" ? kizlo.on_sale_to : null),
699
+ seo: isRecord$1(kizlo.seo) ? deserializeSeo(kizlo.seo) : null,
700
+ custom: productCustomFields(kizlo.custom),
701
+ recommendations
702
+ };
703
+ }
704
+ function deserializeProductSummary(data) {
705
+ const { extensions, kizlo } = deserializeExtensions(data.extensions);
706
+ const termUrls = deserializeTermUrls(kizlo.term_urls);
707
+ return {
708
+ id: data.id,
709
+ name: data.name,
710
+ slug: data.slug,
711
+ parentId: data.parent === 0 ? null : data.parent,
712
+ type: data.type,
713
+ variationDescription: data.variation,
714
+ url: typeof kizlo.url === "string" ? kizlo.url : null,
715
+ sku: data.sku === "" ? null : data.sku,
716
+ shortDescription: data.short_description,
717
+ description: data.description,
718
+ isPasswordProtected: data.is_password_protected,
719
+ isOnSale: data.on_sale,
720
+ prices: {
721
+ price: Number(data.prices.price),
722
+ regularPrice: Number(data.prices.regular_price),
723
+ salePrice: data.on_sale ? Number(data.prices.sale_price) : null,
724
+ priceRange: data.prices.price_range ? {
725
+ minAmount: Number(data.prices.price_range.min_amount),
726
+ maxAmount: Number(data.prices.price_range.max_amount)
727
+ } : null
728
+ },
729
+ currencyFormat: deserializeCurrencyFormat(data.prices),
730
+ priceHtml: data.price_html,
731
+ averageRating: Number(data.average_rating),
732
+ reviewCount: data.review_count,
733
+ images: data.images.map((image) => ({
734
+ type: "image",
735
+ id: image.id,
736
+ src: image.src,
737
+ srcset: image.srcset,
738
+ name: image.name,
739
+ alt: image.alt
740
+ })),
741
+ categories: data.categories.map((term) => deserializeTermRef(term, "product_cat", termUrls)),
742
+ tags: data.tags.map((term) => deserializeTermRef(term, "product_tag", termUrls)),
743
+ brands: data.brands.map((term) => deserializeTermRef(term, "product_brand", termUrls)),
744
+ attributes: data.attributes.map((attribute) => ({
745
+ id: attribute.id,
746
+ name: attribute.name,
747
+ taxonomy: attribute.taxonomy ?? null,
748
+ hasVariations: attribute.has_variations,
749
+ terms: attribute.terms.map((term) => ({
750
+ id: term.id,
751
+ name: term.name,
752
+ slug: term.slug,
753
+ isDefault: term.default ?? false
754
+ }))
755
+ })),
756
+ variations: data.variations.map((variation) => ({
757
+ id: variation.id,
758
+ attributes: variation.attributes.map((attribute) => ({
759
+ name: attribute.name,
760
+ value: attribute.value ?? null
761
+ }))
762
+ })),
763
+ groupedProductIds: data.grouped_products,
764
+ hasOptions: data.has_options,
765
+ isPurchasable: data.is_purchasable,
766
+ isInStock: data.is_in_stock,
767
+ isOnBackorder: data.is_on_backorder,
768
+ stockAvailability: data.stock_availability,
769
+ lowStockRemaining: data.low_stock_remaining,
770
+ isSoldIndividually: data.sold_individually,
771
+ addToCart: {
772
+ text: data.add_to_cart.text,
773
+ description: data.add_to_cart.description,
774
+ singleText: data.add_to_cart.single_text,
775
+ minimum: data.add_to_cart.minimum,
776
+ maximum: data.add_to_cart.maximum,
777
+ multipleOf: data.add_to_cart.multiple_of
778
+ },
779
+ extensions
780
+ };
781
+ }
782
+ function deserializeProductRecommendations(data) {
783
+ return {
784
+ upsells: deserializeEmbeddedProducts(data._embedded?.upsells),
785
+ crossSells: deserializeEmbeddedProducts(data._embedded?.cross_sells),
786
+ related: deserializeEmbeddedProducts(data._embedded?.related)
787
+ };
788
+ }
789
+ function productCustomFields(value) {
790
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
791
+ }
792
+ function deserializeProductFilters(data) {
793
+ if (!data.price_range) return null;
794
+ const maxPrice = +data.price_range.max_price;
795
+ const minPrice = +data.price_range.min_price;
796
+ return {
797
+ ratingCounts: (data.rating_counts ?? []).flatMap((entry) => PRODUCT_RATINGS.includes(entry.rating) ? [{
798
+ count: entry.count,
799
+ rating: entry.rating
800
+ }] : []),
801
+ stockStatuses: (data.stock_status_counts ?? []).flatMap((entry) => {
802
+ const status = stockStatus(entry.status);
803
+ return status ? [{
804
+ count: entry.count,
805
+ status
806
+ }] : [];
807
+ }),
808
+ taxonomyTerms: data.kizlo.taxonomy_counts.map((item) => ({
809
+ id: item.id,
810
+ name: item.name,
811
+ count: item.count,
812
+ description: item.description,
813
+ parentId: item.parent,
814
+ slug: item.slug,
815
+ taxonomy: item.taxonomy,
816
+ image: item.image
817
+ })),
818
+ attributeTerms: data.kizlo.attribute_counts.map((item) => ({
819
+ id: item.id,
820
+ name: item.name,
821
+ count: item.count,
822
+ description: item.description,
823
+ parentId: item.parent,
824
+ slug: item.slug,
825
+ swatch: item.swatch,
826
+ type: item.swatch_type,
827
+ taxonomy: item.taxonomy
828
+ })),
829
+ priceRange: {
830
+ maxPrice,
831
+ minPrice
832
+ },
833
+ currencyFormat: deserializeCurrencyFormat(data.price_range)
834
+ };
835
+ }
836
+ function serializeProductListInput(data) {
837
+ const searchParams = {
838
+ after: data?.after,
839
+ attribute_relation: data?.attributeRelation,
840
+ before: data?.before,
841
+ brand: commaSeparated(data?.brand),
842
+ brand_operator: data?.brandOperator,
843
+ catalog_visibility: data?.catalogVisibility,
844
+ category: commaSeparated(data?.category),
845
+ category_operator: data?.categoryOperator,
846
+ date_column: data?.dateColumn,
847
+ featured: data?.featured,
848
+ max_price: data?.maxPrice === void 0 ? void 0 : String(data.maxPrice),
849
+ min_price: data?.minPrice === void 0 ? void 0 : String(data.minPrice),
850
+ on_sale: data?.onSale,
851
+ orderby: data?.orderBy,
852
+ parent: normalizeArrayableValue(data?.parent),
853
+ parent_exclude: normalizeArrayableValue(data?.parentExclude),
854
+ rating: normalizeArrayableValue(data?.rating),
855
+ sku: commaSeparated(data?.sku),
856
+ slug: commaSeparated(data?.slug),
857
+ stock_status: normalizeArrayableValue(data?.stockStatus),
858
+ tag: commaSeparated(data?.tag),
859
+ tag_operator: data?.tagOperator,
860
+ type: data?.type,
861
+ attributes: data?.attributes?.map((item) => ({
862
+ operator: item.operator,
863
+ attribute: item.taxonomy,
864
+ slug: normalizeArrayableValue(item.slug),
865
+ term_id: normalizeArrayableValue(item.termId)
866
+ })),
867
+ exclude: normalizeArrayableValue(data?.exclude),
868
+ include: normalizeArrayableValue(data?.include),
869
+ offset: data?.offset,
870
+ order: data?.order,
871
+ page: data?.page,
872
+ per_page: data?.perPage,
873
+ related: data?.related,
874
+ search: data?.search
875
+ };
876
+ for (const filter of data?.taxonomies ?? []) {
877
+ const key = `_unstable_tax_${filter.taxonomy}`;
878
+ searchParams[key] = commaSeparated(filter.termIds ?? filter.slugs);
879
+ if (filter.operator !== void 0) searchParams[`${key}_operator`] = filter.operator;
880
+ }
881
+ return searchParams;
882
+ }
883
+ function commaSeparated(value) {
884
+ if (value === void 0) return void 0;
885
+ return (Array.isArray(value) ? value : [value]).join(",");
886
+ }
887
+ function stockStatus(status) {
888
+ return PRODUCT_STOCK_STATUSES.includes(status) ? status : null;
889
+ }
890
+ function deserializeTermRef(term, taxonomy, urls) {
891
+ return {
892
+ id: term.id,
893
+ name: term.name,
894
+ slug: term.slug,
895
+ url: urls[`${taxonomy}:${term.id}`] ?? null
896
+ };
897
+ }
898
+ function deserializeTermUrls(value) {
899
+ if (!Array.isArray(value)) return {};
900
+ return Object.fromEntries(value.flatMap((item) => {
901
+ if (!isRecord$1(item) || typeof item.id !== "number" || typeof item.taxonomy !== "string" || typeof item.url !== "string") return [];
902
+ return [[`${item.taxonomy}:${item.id}`, item.url]];
903
+ }));
904
+ }
905
+ function deserializeEmbeddedProducts(collections) {
906
+ return (collections ?? []).flat().map(deserializeProductSummary);
907
+ }
908
+ function deserializeExtensions(value) {
909
+ const { kizlo: rawKizlo,...extensions } = asRecord(value);
910
+ return {
911
+ extensions,
912
+ kizlo: asRecord(rawKizlo)
913
+ };
914
+ }
915
+ function asRecord(value) {
916
+ return isRecord$1(value) ? value : {};
917
+ }
918
+ function isRecord$1(value) {
919
+ return typeof value === "object" && value !== null && !Array.isArray(value);
920
+ }
921
+ function nullableNumber(value) {
922
+ return typeof value === "number" ? value : null;
923
+ }
924
+
925
+ //#endregion
926
+ //#region src/cart/utils.ts
927
+ function assertNoMissing$2() {}
928
+ assertNoMissing$2();
929
+ assertNoMissing$2();
930
+ assertNoMissing$2();
931
+ assertNoMissing$2();
932
+ assertNoMissing$2();
933
+ assertNoMissing$2();
934
+ 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
+ function deserializeCart(data) {
950
+ const { extensions } = deserializeExtensions(data.extensions);
951
+ return {
952
+ items: data.items.map(deserializeCartItem),
953
+ itemCount: data.items_count,
954
+ itemsWeight: data.items_weight,
955
+ billingAddress: deserializeCartBillingAddress(data.billing_address),
956
+ shippingAddress: deserializeCartShippingAddress(data.shipping_address),
957
+ shippingPackages: data.shipping_rates.map((pkg) => ({
958
+ id: pkg.package_id,
959
+ name: pkg.name,
960
+ destination: {
961
+ address1: pkg.destination.address_1,
962
+ address2: pkg.destination.address_2,
963
+ city: pkg.destination.city,
964
+ state: pkg.destination.state,
965
+ postcode: pkg.destination.postcode,
966
+ country: pkg.destination.country
967
+ },
968
+ items: pkg.items.map((item) => ({
969
+ key: item.key,
970
+ name: item.name,
971
+ quantity: item.quantity
972
+ })),
973
+ rates: pkg.shipping_rates.map((rate) => ({
974
+ id: rate.rate_id,
975
+ name: rate.name,
976
+ description: rate.description,
977
+ deliveryTime: rate.delivery_time,
978
+ price: Number(rate.price),
979
+ taxes: Number(rate.taxes),
980
+ methodId: rate.method_id,
981
+ instanceId: rate.instance_id,
982
+ metadata: rate.meta_data,
983
+ selected: rate.selected
984
+ }))
985
+ })),
986
+ coupons: data.coupons.map((coupon) => ({
987
+ code: coupon.code,
988
+ discountType: coupon.discount_type,
989
+ totals: {
990
+ discount: Number(coupon.totals.total_discount),
991
+ discountTax: Number(coupon.totals.total_discount_tax)
992
+ }
993
+ })),
994
+ fees: data.fees.map((fee) => ({
995
+ id: fee.key,
996
+ name: fee.name,
997
+ totals: {
998
+ total: Number(fee.totals.total),
999
+ tax: Number(fee.totals.total_tax)
1000
+ }
1001
+ })),
1002
+ crossSells: data.cross_sells.map((product) => deserializeProductSummary(product)),
1003
+ needsPayment: data.needs_payment,
1004
+ needsShipping: data.needs_shipping,
1005
+ hasCalculatedShipping: data.has_calculated_shipping,
1006
+ paymentMethods: data.payment_methods,
1007
+ paymentRequirements: data.payment_requirements,
1008
+ errors: data.errors,
1009
+ totals: {
1010
+ itemsTotal: Number(data.totals.total_items),
1011
+ itemsTaxTotal: Number(data.totals.total_items_tax),
1012
+ feesTotal: Number(data.totals.total_fees),
1013
+ feesTaxTotal: Number(data.totals.total_fees_tax),
1014
+ discountTotal: Number(data.totals.total_discount),
1015
+ discountTaxTotal: Number(data.totals.total_discount_tax),
1016
+ shippingTotal: nullableMoney$1(data.totals.total_shipping),
1017
+ shippingTaxTotal: nullableMoney$1(data.totals.total_shipping_tax),
1018
+ total: Number(data.totals.total_price),
1019
+ taxTotal: Number(data.totals.total_tax),
1020
+ taxLines: data.totals.tax_lines.map((line) => ({
1021
+ name: line.name,
1022
+ price: Number(line.price),
1023
+ rate: line.rate
1024
+ }))
1025
+ },
1026
+ currencyFormat: deserializeCurrencyFormat(data.totals),
1027
+ extensions
1028
+ };
1029
+ }
1030
+ function deserializeCartItem(item) {
1031
+ const { extensions, kizlo } = deserializeExtensions(item.extensions);
1032
+ const variationId = typeof kizlo.variation_id === "number" ? kizlo.variation_id : 0;
1033
+ return {
1034
+ key: item.key,
1035
+ productId: typeof kizlo.product_id === "number" ? kizlo.product_id : item.id,
1036
+ variationId: variationId === 0 ? null : variationId,
1037
+ type: item.type,
1038
+ name: item.name,
1039
+ sku: item.sku === "" ? null : item.sku,
1040
+ slug: typeof kizlo.slug === "string" ? kizlo.slug : "",
1041
+ url: typeof kizlo.url === "string" ? kizlo.url : null,
1042
+ shortDescription: item.short_description,
1043
+ description: item.description,
1044
+ quantity: item.quantity,
1045
+ quantityLimits: {
1046
+ minimum: item.quantity_limits.minimum,
1047
+ maximum: item.quantity_limits.maximum,
1048
+ multipleOf: item.quantity_limits.multiple_of,
1049
+ editable: item.quantity_limits.editable
1050
+ },
1051
+ lowStockRemaining: item.low_stock_remaining,
1052
+ allowsBackorders: item.backorders_allowed,
1053
+ showsBackorderBadge: item.show_backorder_badge,
1054
+ isSoldIndividually: item.sold_individually,
1055
+ catalogVisibility: item.catalog_visibility,
1056
+ images: item.images.map((image) => ({
1057
+ type: "image",
1058
+ id: image.id,
1059
+ name: image.name,
1060
+ alt: image.alt,
1061
+ src: image.src,
1062
+ srcset: image.srcset
1063
+ })),
1064
+ selectedAttributes: item.variation.map((attribute) => ({
1065
+ name: attribute.attribute,
1066
+ attribute: attribute.raw_attribute,
1067
+ value: attribute.value
1068
+ })),
1069
+ itemData: item.item_data.map((entry) => ({
1070
+ name: entry.name,
1071
+ value: entry.value,
1072
+ display: entry.display ?? null
1073
+ })),
1074
+ prices: {
1075
+ price: Number(item.prices.price),
1076
+ regularPrice: Number(item.prices.regular_price),
1077
+ salePrice: item.prices.sale_price === "" || item.prices.sale_price === item.prices.regular_price ? null : Number(item.prices.sale_price),
1078
+ priceRange: item.prices.price_range ? {
1079
+ minAmount: Number(item.prices.price_range.min_amount),
1080
+ maxAmount: Number(item.prices.price_range.max_amount)
1081
+ } : null
1082
+ },
1083
+ totals: {
1084
+ subtotal: Number(item.totals.line_subtotal),
1085
+ subtotalTax: Number(item.totals.line_subtotal_tax),
1086
+ total: Number(item.totals.line_total),
1087
+ totalTax: Number(item.totals.line_total_tax)
1088
+ },
1089
+ custom: productCustomFields(kizlo.custom),
1090
+ extensions
1091
+ };
1092
+ }
1093
+ function deserializeCartShippingAddress(address) {
1094
+ return {
1095
+ firstName: address.first_name,
1096
+ lastName: address.last_name,
1097
+ company: address.company,
1098
+ address1: address.address_1,
1099
+ address2: address.address_2,
1100
+ city: address.city,
1101
+ state: address.state,
1102
+ postcode: address.postcode,
1103
+ country: address.country,
1104
+ phone: address.phone,
1105
+ additionalFields: additionalAddressFields(address, SHIPPING_ADDRESS_KEYS)
1106
+ };
1107
+ }
1108
+ function deserializeCartBillingAddress(address) {
1109
+ return {
1110
+ ...deserializeCartShippingAddress(address),
1111
+ email: address.email,
1112
+ additionalFields: additionalAddressFields(address, BILLING_ADDRESS_KEYS)
1113
+ };
1114
+ }
1115
+ const SHIPPING_ADDRESS_KEYS = new Set([
1116
+ "first_name",
1117
+ "last_name",
1118
+ "company",
1119
+ "address_1",
1120
+ "address_2",
1121
+ "city",
1122
+ "state",
1123
+ "postcode",
1124
+ "country",
1125
+ "phone"
1126
+ ]);
1127
+ const BILLING_ADDRESS_KEYS = new Set([...SHIPPING_ADDRESS_KEYS, "email"]);
1128
+ function additionalAddressFields(address, standardKeys) {
1129
+ return Object.fromEntries(Object.entries(address).filter((entry) => !standardKeys.has(entry[0]) && (typeof entry[1] === "string" || typeof entry[1] === "boolean")));
1130
+ }
1131
+ function serializeCartUpdateInput(input) {
1132
+ return {
1133
+ ...input.billingAddress !== void 0 && { billing_address: serializeCartBillingAddress(input.billingAddress) },
1134
+ ...input.shippingAddress !== void 0 && { shipping_address: serializeCartShippingAddress(input.shippingAddress) }
1135
+ };
1136
+ }
1137
+ function serializeCartShippingAddress(address) {
1138
+ return compactAddress({
1139
+ ...address.additionalFields,
1140
+ first_name: address.firstName,
1141
+ last_name: address.lastName,
1142
+ company: address.company,
1143
+ address_1: address.address1,
1144
+ address_2: address.address2,
1145
+ city: address.city,
1146
+ state: address.state,
1147
+ postcode: address.postcode,
1148
+ country: address.country,
1149
+ phone: address.phone
1150
+ });
1151
+ }
1152
+ function serializeCartBillingAddress(address) {
1153
+ return compactAddress({
1154
+ ...serializeCartShippingAddress(address),
1155
+ email: address.email
1156
+ });
1157
+ }
1158
+ function compactAddress(address) {
1159
+ return Object.fromEntries(Object.entries(address).filter(([, value]) => value !== void 0));
1160
+ }
1161
+ function nullableMoney$1(value) {
1162
+ return value === null ? null : Number(value);
1163
+ }
1164
+
1165
+ //#endregion
1166
+ //#region src/cart/index.ts
1167
+ const CART_PROCEDURES = {
1168
+ get: createProcedure({
1169
+ scope: "api",
1170
+ method: "GET",
1171
+ path: "/cart",
1172
+ output: Cart,
1173
+ errors: GET_CART_ERROR_MAP,
1174
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
1175
+ }, async ({ context, errors }) => {
1176
+ const response = await context.wordpress.woocommerce.store.cart.get({}, { headers: context.sessionHeaders });
1177
+ if (response.error) switch (response.error.code) {
541
1178
  default:
542
1179
  context.logger.error("Get cart unhandled error", response.error, { code: response.error.code });
543
1180
  throw errors.INTERNAL_SERVER_ERROR();
@@ -551,45 +1188,15 @@ const CART_PROCEDURES = {
551
1188
  output: Cart,
552
1189
  body: UpdateCartInput,
553
1190
  errors: UPDATE_CART_ERROR_MAP,
554
- middlewares: [sessionMiddleware()]
1191
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
555
1192
  }, 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 });
1193
+ const response = await context.wordpress.woocommerce.store.cart.updateCustomer(serializeCartUpdateInput(input), { headers: context.sessionHeaders });
592
1194
  if (response.error) switch (response.error.code) {
1195
+ case "rest_invalid_param":
1196
+ case "woocommerce_rest_invalid_address":
1197
+ case "woocommerce_rest_invalid_address_country":
1198
+ case "woocommerce_rest_invalid_email_address":
1199
+ case "woocommerce_rest_missing_email_address": throw errors.CART_ADDRESS_INVALID({ message: response.error.message });
593
1200
  default:
594
1201
  context.logger.error("Update cart customer unhandled error", response.error, { code: response.error.code });
595
1202
  throw errors.INTERNAL_SERVER_ERROR();
@@ -603,7 +1210,7 @@ const CART_PROCEDURES = {
603
1210
  body: SelectCartShippingRateInput,
604
1211
  output: Cart,
605
1212
  errors: SELECT_SHIPPING_RATE_ERROR_MAP,
606
- middlewares: [sessionMiddleware()]
1213
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
607
1214
  }, async ({ context, input: { body }, errors }) => {
608
1215
  const response = await context.wordpress.woocommerce.store.cart.selectShippingRate({
609
1216
  rate_id: body.rateId,
@@ -626,12 +1233,12 @@ const CART_PROCEDURES = {
626
1233
  body: AddCartItemInput,
627
1234
  output: Cart,
628
1235
  errors: ADD_CART_ITEM_ERROR_MAP,
629
- middlewares: [sessionMiddleware()]
1236
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
630
1237
  }, async ({ context, input: { body: input }, errors }) => {
631
1238
  const response = await context.wordpress.woocommerce.store.cart.addItem({
632
- id: input.productId,
1239
+ id: input.variationId ?? input.productId,
633
1240
  quantity: input.quantity,
634
- variation: input.variations ?? []
1241
+ variation: input.selectedAttributes ?? []
635
1242
  }, { headers: context.sessionHeaders });
636
1243
  if (response.error) switch (response.error.code) {
637
1244
  case "woocommerce_rest_product_out_of_stock": throw errors.CART_ITEM_OUT_OF_STOCK({ message: response.error.message });
@@ -659,7 +1266,7 @@ const CART_PROCEDURES = {
659
1266
  body: UpdateCartItemInput.pick({ quantity: true }),
660
1267
  output: Cart,
661
1268
  errors: UPDATE_CART_ITEM_ERROR_MAP,
662
- middlewares: [sessionMiddleware()]
1269
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
663
1270
  }, async ({ context, input: { params, body }, errors }) => {
664
1271
  const response = await context.wordpress.woocommerce.store.cart.updateItem({
665
1272
  key: params.key,
@@ -679,12 +1286,12 @@ const CART_PROCEDURES = {
679
1286
  }),
680
1287
  remove: createProcedure({
681
1288
  scope: "api",
682
- method: "PATCH",
1289
+ method: "DELETE",
683
1290
  path: "/cart/items/{key}",
684
1291
  params: RemoveCartItemInput.pick({ key: true }),
685
1292
  output: Cart,
686
1293
  errors: REMOVE_CART_ITEM_ERROR_MAP,
687
- middlewares: [sessionMiddleware()]
1294
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
688
1295
  }, async ({ context, input: { params }, errors }) => {
689
1296
  const response = await context.wordpress.woocommerce.store.cart.removeItem({ key: params.key }, { headers: context.sessionHeaders });
690
1297
  if (response.error) switch (response.error.code) {
@@ -704,7 +1311,7 @@ const CART_PROCEDURES = {
704
1311
  body: ApplyCouponInput,
705
1312
  output: Cart,
706
1313
  errors: APPLY_COUPON_ERROR_MAP,
707
- middlewares: [sessionMiddleware()]
1314
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
708
1315
  }, async ({ context, input: { body }, errors }) => {
709
1316
  const response = await context.wordpress.woocommerce.store.cart.applyCoupon({ code: body.code }, { headers: context.sessionHeaders });
710
1317
  if (response.error) switch (response.error.code) {
@@ -718,12 +1325,12 @@ const CART_PROCEDURES = {
718
1325
  }),
719
1326
  remove: createProcedure({
720
1327
  scope: "api",
721
- method: "POST",
1328
+ method: "DELETE",
722
1329
  path: "/cart/coupons/{code}",
723
1330
  params: RemoveCouponInput.pick({ code: true }),
724
1331
  output: Cart,
725
1332
  errors: REMOVE_COUPON_ERROR_MAP,
726
- middlewares: [sessionMiddleware()]
1333
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
727
1334
  }, async ({ context, input, errors }) => {
728
1335
  const response = await context.wordpress.woocommerce.store.cart.removeCoupon({ code: input.params.code }, { headers: context.sessionHeaders });
729
1336
  if (response.error) switch (response.error.code) {
@@ -741,6 +1348,8 @@ const CART_PROCEDURES = {
741
1348
 
742
1349
  //#endregion
743
1350
  //#region src/checkout/error.ts
1351
+ const CheckoutValidationData = z$2.object({ fields: z$2.record(z$2.string(), z$2.string()) });
1352
+ const CheckoutConflictData = z$2.object({ cart: Cart.nullable() });
744
1353
  const GET_CHECKOUT_ERROR_MAP = defineErrorMap({ CHECKOUT_ORDER_NOT_FOUND: {
745
1354
  status: 404,
746
1355
  message: "No checkout order found."
@@ -780,7 +1389,20 @@ const CONFIRM_CHECKOUT_ERROR_MAP = defineErrorMap({
780
1389
  },
781
1390
  CHECKOUT_VALIDATION_FAILED: {
782
1391
  status: 400,
783
- message: "Checkout validation failed."
1392
+ message: "Checkout validation failed.",
1393
+ data: CheckoutValidationData
1394
+ },
1395
+ CHECKOUT_ACCOUNT_CREATION_FAILED: {
1396
+ status: 400,
1397
+ message: "The customer account could not be created."
1398
+ },
1399
+ CHECKOUT_PAYMENT_RESULT_INVALID: {
1400
+ status: 500,
1401
+ message: "The payment gateway returned an invalid result."
1402
+ },
1403
+ CHECKOUT_ORDER_CREATION_FAILED: {
1404
+ status: 500,
1405
+ message: "The checkout order could not be created."
784
1406
  },
785
1407
  CHECKOUT_GUEST_DISABLED: {
786
1408
  status: 403,
@@ -792,31 +1414,38 @@ const CONFIRM_CHECKOUT_ERROR_MAP = defineErrorMap({
792
1414
  },
793
1415
  CHECKOUT_CART_EMPTY: {
794
1416
  status: 409,
795
- message: "The cart is empty."
1417
+ message: "The cart is empty.",
1418
+ data: CheckoutConflictData
796
1419
  },
797
1420
  CHECKOUT_CART_INVALID: {
798
1421
  status: 409,
799
- message: "An item in the cart is no longer valid."
1422
+ message: "An item in the cart is no longer valid.",
1423
+ data: CheckoutConflictData
800
1424
  },
801
1425
  CHECKOUT_COUPONS_REMOVED: {
802
1426
  status: 409,
803
- message: "One or more coupons were removed from the cart."
1427
+ message: "One or more coupons were removed from the cart.",
1428
+ data: CheckoutConflictData
804
1429
  },
805
1430
  CHECKOUT_COUPON_RESERVATION_FAILED: {
806
1431
  status: 409,
807
- message: "A coupon could not be reserved for this order."
1432
+ message: "A coupon could not be reserved for this order.",
1433
+ data: CheckoutConflictData
808
1434
  },
809
1435
  CHECKOUT_PRODUCT_INSUFFICIENT_STOCK: {
810
1436
  status: 409,
811
- message: "Not enough stock for one or more items in the cart."
1437
+ message: "Not enough stock for one or more items in the cart.",
1438
+ data: CheckoutConflictData
812
1439
  },
813
1440
  CHECKOUT_PRODUCT_NOT_PURCHASABLE: {
814
1441
  status: 409,
815
- message: "An item in the cart is no longer purchasable."
1442
+ message: "An item in the cart is no longer purchasable.",
1443
+ data: CheckoutConflictData
816
1444
  },
817
1445
  CHECKOUT_PRODUCT_OUT_OF_STOCK: {
818
1446
  status: 409,
819
- message: "An item in the cart is out of stock."
1447
+ message: "An item in the cart is out of stock.",
1448
+ data: CheckoutConflictData
820
1449
  }
821
1450
  });
822
1451
  const RETRY_CHECKOUT_ERROR_MAP = defineErrorMap({
@@ -836,6 +1465,15 @@ const RETRY_CHECKOUT_ERROR_MAP = defineErrorMap({
836
1465
  status: 400,
837
1466
  message: "A payment method is required."
838
1467
  },
1468
+ CHECKOUT_VALIDATION_FAILED: {
1469
+ status: 400,
1470
+ message: "Checkout validation failed.",
1471
+ data: CheckoutValidationData
1472
+ },
1473
+ CHECKOUT_PAYMENT_RESULT_INVALID: {
1474
+ status: 500,
1475
+ message: "The payment gateway returned an invalid result."
1476
+ },
839
1477
  CHECKOUT_ORDER_FORBIDDEN: {
840
1478
  status: 403,
841
1479
  message: "You are not allowed to pay for this order."
@@ -858,191 +1496,145 @@ const UPDATE_CHECKOUT_ERROR_MAP = defineErrorMap({
858
1496
  status: 400,
859
1497
  message: "The selected payment method is not available."
860
1498
  },
1499
+ CHECKOUT_VALIDATION_FAILED: {
1500
+ status: 400,
1501
+ message: "Checkout validation failed.",
1502
+ data: CheckoutValidationData
1503
+ },
861
1504
  CHECKOUT_ORDER_NOT_FOUND: {
862
1505
  status: 404,
863
1506
  message: "No checkout order found."
864
1507
  },
865
1508
  CHECKOUT_CART_EMPTY: {
866
1509
  status: 409,
867
- message: "The cart is empty."
1510
+ message: "The cart is empty.",
1511
+ data: CheckoutConflictData
868
1512
  },
869
1513
  CHECKOUT_CART_INVALID: {
870
1514
  status: 409,
871
- message: "An item in the cart is no longer valid."
1515
+ message: "An item in the cart is no longer valid.",
1516
+ data: CheckoutConflictData
872
1517
  },
873
1518
  CHECKOUT_PRODUCT_INSUFFICIENT_STOCK: {
874
1519
  status: 409,
875
- message: "Not enough stock for one or more items in the cart."
1520
+ message: "Not enough stock for one or more items in the cart.",
1521
+ data: CheckoutConflictData
876
1522
  },
877
1523
  CHECKOUT_PRODUCT_NOT_PURCHASABLE: {
878
1524
  status: 409,
879
- message: "An item in the cart is no longer purchasable."
1525
+ message: "An item in the cart is no longer purchasable.",
1526
+ data: CheckoutConflictData
880
1527
  },
881
1528
  CHECKOUT_PRODUCT_OUT_OF_STOCK: {
882
1529
  status: 409,
883
- message: "An item in the cart is out of stock."
1530
+ message: "An item in the cart is out of stock.",
1531
+ data: CheckoutConflictData
884
1532
  }
885
1533
  });
886
1534
 
887
1535
  //#endregion
888
1536
  //#region src/checkout/schema.ts
889
1537
  const CheckoutAdditionalFields = z$2.record(z$2.string(), z$2.union([z$2.string(), z$2.boolean()]));
1538
+ const CheckoutExtensions = z$2.record(z$2.string(), z$2.unknown());
890
1539
  const CheckoutPaymentData = z$2.array(z$2.object({
891
1540
  key: z$2.string(),
892
1541
  value: z$2.union([z$2.string(), z$2.boolean()])
893
1542
  }));
1543
+ const CheckoutPaymentResult = z$2.object({
1544
+ status: z$2.string(),
1545
+ details: z$2.array(z$2.object({
1546
+ key: z$2.string(),
1547
+ value: z$2.string()
1548
+ })),
1549
+ redirectUrl: z$2.string().nullable()
1550
+ });
894
1551
  const Checkout = z$2.object({
895
- cart: Cart.nullable(),
896
- billingAddress: BillingAddress,
897
- shippingAddress: ShippingAddress,
898
- paymentMethod: z$2.string(),
1552
+ orderId: z$2.number().nullable(),
1553
+ orderNumber: z$2.string().nullable(),
1554
+ orderKey: z$2.string().nullable(),
1555
+ status: z$2.string(),
1556
+ customerId: z$2.number().nullable(),
899
1557
  customerNote: z$2.string(),
1558
+ billingAddress: CartBillingAddress,
1559
+ shippingAddress: CartShippingAddress,
1560
+ paymentMethod: z$2.string().nullable(),
1561
+ paymentResult: CheckoutPaymentResult.nullable(),
900
1562
  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()
1563
+ cart: Cart.nullable(),
1564
+ extensions: CheckoutExtensions
914
1565
  });
915
1566
  const UpdateCheckoutInput = z$2.object({
916
1567
  paymentMethod: z$2.string().optional(),
917
1568
  customerNote: z$2.string().optional(),
918
1569
  recalculateTotals: z$2.boolean().optional(),
919
- additionalFields: CheckoutAdditionalFields.optional()
1570
+ additionalFields: CheckoutAdditionalFields.optional(),
1571
+ extensions: CheckoutExtensions.optional()
920
1572
  });
921
1573
  const ConfirmCheckoutInput = z$2.object({
1574
+ billingAddress: CartBillingAddress,
1575
+ shippingAddress: CartShippingAddress.optional(),
1576
+ paymentMethod: z$2.string(),
1577
+ customerNote: z$2.string().optional(),
1578
+ createAccount: z$2.boolean().optional(),
922
1579
  customerPassword: z$2.string().optional(),
923
- paymentData: CheckoutPaymentData.optional()
1580
+ paymentData: CheckoutPaymentData.optional(),
1581
+ additionalFields: CheckoutAdditionalFields.optional(),
1582
+ extensions: CheckoutExtensions.optional()
924
1583
  });
925
1584
  const RetryCheckoutInput = z$2.object({
926
1585
  key: z$2.string(),
927
1586
  orderId: NumberLike,
928
1587
  paymentMethod: z$2.string(),
929
1588
  billingEmail: z$2.email().optional(),
930
- billingAddress: BillingAddress,
1589
+ billingAddress: CartBillingAddress,
931
1590
  paymentData: CheckoutPaymentData.optional(),
932
- shippingAddress: ShippingAddress.optional()
1591
+ shippingAddress: CartShippingAddress.optional(),
1592
+ customerNote: z$2.string().optional(),
1593
+ additionalFields: CheckoutAdditionalFields.optional(),
1594
+ extensions: CheckoutExtensions.optional()
933
1595
  });
934
1596
 
935
1597
  //#endregion
936
1598
  //#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
- */
1599
+ function assertNoMissing$1() {}
1600
+ assertNoMissing$1();
1601
+ assertNoMissing$1();
1602
+ assertNoMissing$1();
1603
+ assertNoMissing$1();
949
1604
  function gateway(method) {
950
1605
  return method;
951
1606
  }
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
1607
  function deserializeCheckout(data) {
1608
+ const { extensions } = deserializeExtensions(data.extensions);
1609
+ const paymentResult = data.payment_result;
986
1610
  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),
1611
+ orderId: data.order_id === 0 ? null : data.order_id,
1612
+ orderNumber: data.order_number === "" || data.order_number === "0" ? null : data.order_number,
1613
+ orderKey: data.order_key === "" ? null : data.order_key,
1614
+ status: data.status,
1615
+ customerId: data.customer_id === 0 ? null : data.customer_id,
1014
1616
  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
1617
+ billingAddress: deserializeCartBillingAddress(data.billing_address),
1618
+ shippingAddress: deserializeCartShippingAddress(data.shipping_address),
1619
+ paymentMethod: data.payment_method === "" ? null : data.payment_method,
1620
+ paymentResult: paymentResult?.payment_status === void 0 || paymentResult.payment_status === "" ? null : {
1621
+ status: paymentResult.payment_status,
1622
+ details: paymentResult.payment_details,
1623
+ redirectUrl: paymentResult.redirect_url === "" ? null : paymentResult.redirect_url
1624
+ },
1625
+ additionalFields: checkoutAdditionalFields(data.additional_fields),
1626
+ cart: data.__experimentalCart ? deserializeCart(data.__experimentalCart) : null,
1627
+ extensions
1021
1628
  };
1022
1629
  }
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;
1630
+ function serializeCheckoutShippingAddress(address) {
1631
+ return serializeCartShippingAddress(address);
1033
1632
  }
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
- }
1633
+ function serializeCheckoutBillingAddress(address) {
1634
+ return serializeCartBillingAddress(address);
1635
+ }
1636
+ function checkoutAdditionalFields(fields) {
1637
+ return Object.fromEntries(Object.entries(fields ?? {}).filter((entry) => typeof entry[1] === "string" || typeof entry[1] === "boolean"));
1046
1638
  }
1047
1639
 
1048
1640
  //#endregion
@@ -1054,14 +1646,13 @@ const CHECKOUT_PROCEDURES = {
1054
1646
  path: "/checkout",
1055
1647
  output: Checkout,
1056
1648
  errors: GET_CHECKOUT_ERROR_MAP,
1057
- middlewares: [sessionMiddleware()]
1649
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
1058
1650
  }, async ({ context, errors }) => {
1059
1651
  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();
1652
+ if (response.error) {
1653
+ if (response.error.code === "woocommerce_rest_checkout_missing_order") throw errors.CHECKOUT_ORDER_NOT_FOUND({ message: response.error.message });
1654
+ context.logger.error("Get checkout unhandled error", response.error, { code: response.error.code });
1655
+ throw errors.INTERNAL_SERVER_ERROR();
1065
1656
  }
1066
1657
  return deserializeCheckout(response.data);
1067
1658
  }),
@@ -1072,26 +1663,49 @@ const CHECKOUT_PROCEDURES = {
1072
1663
  body: UpdateCheckoutInput,
1073
1664
  output: Checkout,
1074
1665
  errors: UPDATE_CHECKOUT_ERROR_MAP,
1075
- middlewares: [sessionMiddleware()]
1666
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
1076
1667
  }, async ({ context, input, errors }) => {
1077
1668
  const response = await context.wordpress.woocommerce.store.checkout.update({
1078
1669
  order_notes: input.body.customerNote,
1079
1670
  payment_method: gateway(input.body.paymentMethod),
1080
1671
  additional_fields: input.body.additionalFields,
1672
+ extensions: input.body.extensions,
1081
1673
  __experimental_calc_totals: input.body.recalculateTotals
1082
1674
  }, { 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();
1675
+ if (response.error) {
1676
+ const conflict = conflictData(response.error.data);
1677
+ switch (response.error.code) {
1678
+ case "rest_invalid_param": throw errors.CHECKOUT_VALIDATION_FAILED({
1679
+ message: response.error.message,
1680
+ data: { fields: validationFields(response.error.data) }
1681
+ });
1682
+ case "woocommerce_rest_cart_coupon_error": throw errors.CHECKOUT_COUPON_INVALID({ message: response.error.message });
1683
+ case "woocommerce_rest_checkout_payment_method_disabled": throw errors.CHECKOUT_PAYMENT_METHOD_DISABLED({ message: response.error.message });
1684
+ case "woocommerce_rest_checkout_missing_order": throw errors.CHECKOUT_ORDER_NOT_FOUND({ message: response.error.message });
1685
+ case "woocommerce_rest_cart_empty": throw errors.CHECKOUT_CART_EMPTY({
1686
+ message: response.error.message,
1687
+ data: conflict
1688
+ });
1689
+ case "woocommerce_rest_cart_item_error": throw errors.CHECKOUT_CART_INVALID({
1690
+ message: response.error.message,
1691
+ data: conflict
1692
+ });
1693
+ case "woocommerce_rest_product_partially_out_of_stock": throw errors.CHECKOUT_PRODUCT_INSUFFICIENT_STOCK({
1694
+ message: response.error.message,
1695
+ data: conflict
1696
+ });
1697
+ case "woocommerce_rest_product_not_purchasable": throw errors.CHECKOUT_PRODUCT_NOT_PURCHASABLE({
1698
+ message: response.error.message,
1699
+ data: conflict
1700
+ });
1701
+ case "woocommerce_rest_product_out_of_stock": throw errors.CHECKOUT_PRODUCT_OUT_OF_STOCK({
1702
+ message: response.error.message,
1703
+ data: conflict
1704
+ });
1705
+ default:
1706
+ context.logger.error("Update checkout unhandled error", response.error, { code: response.error.code });
1707
+ throw errors.INTERNAL_SERVER_ERROR();
1708
+ }
1095
1709
  }
1096
1710
  return deserializeCheckout(response.data);
1097
1711
  }),
@@ -1102,49 +1716,78 @@ const CHECKOUT_PROCEDURES = {
1102
1716
  body: ConfirmCheckoutInput,
1103
1717
  output: Checkout,
1104
1718
  errors: CONFIRM_CHECKOUT_ERROR_MAP,
1105
- middlewares: [sessionMiddleware()]
1719
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
1106
1720
  }, 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,
1721
+ const response = await context.wordpress.woocommerce.store.checkout.process({
1722
+ billing_address: serializeCheckoutBillingAddress(input.body.billingAddress),
1723
+ shipping_address: input.body.shippingAddress ? serializeCheckoutShippingAddress(input.body.shippingAddress) : void 0,
1724
+ payment_method: gateway(input.body.paymentMethod),
1725
+ customer_note: input.body.customerNote,
1726
+ create_account: input.body.createAccount ?? false,
1116
1727
  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
1728
+ payment_data: input.body.paymentData,
1729
+ additional_fields: input.body.additionalFields,
1730
+ extensions: input.body.extensions
1123
1731
  }, { 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();
1732
+ if (response.error) {
1733
+ const conflict = conflictData(response.error.data);
1734
+ switch (response.error.code) {
1735
+ case "rest_invalid_param": throw errors.CHECKOUT_VALIDATION_FAILED({
1736
+ message: response.error.message,
1737
+ data: { fields: validationFields(response.error.data) }
1738
+ });
1739
+ case "woocommerce_rest_invalid_address": throw errors.CHECKOUT_ADDRESS_INVALID({ message: response.error.message });
1740
+ case "woocommerce_rest_invalid_address_country": throw errors.CHECKOUT_ADDRESS_COUNTRY_INVALID({ message: response.error.message });
1741
+ case "woocommerce_rest_cart_coupon_error": throw errors.CHECKOUT_COUPON_INVALID({ message: response.error.message });
1742
+ case "woocommerce_rest_invalid_email_address": throw errors.CHECKOUT_EMAIL_INVALID({ message: response.error.message });
1743
+ case "woocommerce_rest_missing_email_address": throw errors.CHECKOUT_EMAIL_MISSING({ message: response.error.message });
1744
+ case "woocommerce_rest_checkout_process_payment_error": throw errors.CHECKOUT_PAYMENT_FAILED({ message: response.error.message });
1745
+ case "woocommerce_rest_checkout_payment_method_disabled": throw errors.CHECKOUT_PAYMENT_METHOD_DISABLED({ message: response.error.message });
1746
+ case "woocommerce_rest_checkout_missing_payment_method": throw errors.CHECKOUT_PAYMENT_METHOD_MISSING({ message: response.error.message });
1747
+ case "woocommerce_rest_checkout_custom_validation_error": throw errors.CHECKOUT_VALIDATION_FAILED({
1748
+ message: response.error.message,
1749
+ data: { fields: {} }
1750
+ });
1751
+ case "woocommerce_rest_checkout_invalid_payment_result": throw errors.CHECKOUT_PAYMENT_RESULT_INVALID({ message: response.error.message });
1752
+ case "woocommerce_rest_guest_checkout_disabled": throw errors.CHECKOUT_GUEST_DISABLED({ message: response.error.message });
1753
+ case "woocommerce_rest_checkout_missing_order":
1754
+ if (response.status >= 500) throw errors.CHECKOUT_ORDER_CREATION_FAILED({ message: response.error.message });
1755
+ throw errors.CHECKOUT_ORDER_NOT_FOUND({ message: response.error.message });
1756
+ case "woocommerce_rest_cart_empty": throw errors.CHECKOUT_CART_EMPTY({
1757
+ message: response.error.message,
1758
+ data: conflict
1759
+ });
1760
+ case "woocommerce_rest_cart_item_error": throw errors.CHECKOUT_CART_INVALID({
1761
+ message: response.error.message,
1762
+ data: conflict
1763
+ });
1764
+ case "removed_coupons": throw errors.CHECKOUT_COUPONS_REMOVED({
1765
+ message: response.error.message,
1766
+ data: conflict
1767
+ });
1768
+ case "woocommerce_rest_coupon_reserve_failed": throw errors.CHECKOUT_COUPON_RESERVATION_FAILED({
1769
+ message: response.error.message,
1770
+ data: conflict
1771
+ });
1772
+ case "woocommerce_rest_product_partially_out_of_stock": throw errors.CHECKOUT_PRODUCT_INSUFFICIENT_STOCK({
1773
+ message: response.error.message,
1774
+ data: conflict
1775
+ });
1776
+ case "woocommerce_rest_product_not_purchasable": throw errors.CHECKOUT_PRODUCT_NOT_PURCHASABLE({
1777
+ message: response.error.message,
1778
+ data: conflict
1779
+ });
1780
+ case "woocommerce_rest_product_out_of_stock": throw errors.CHECKOUT_PRODUCT_OUT_OF_STOCK({
1781
+ message: response.error.message,
1782
+ data: conflict
1783
+ });
1784
+ default:
1785
+ if (input.body.createAccount && response.status === 400) throw errors.CHECKOUT_ACCOUNT_CREATION_FAILED({ message: response.error.message });
1786
+ context.logger.error("Confirm checkout unhandled error", response.error, { code: response.error.code });
1787
+ throw errors.INTERNAL_SERVER_ERROR();
1788
+ }
1146
1789
  }
1147
- return deserializeCheckout(confirmResponse.data);
1790
+ return deserializeCheckout(response.data);
1148
1791
  }),
1149
1792
  retry: createProcedure({
1150
1793
  scope: "api",
@@ -1154,7 +1797,7 @@ const CHECKOUT_PROCEDURES = {
1154
1797
  body: RetryCheckoutInput.omit({ orderId: true }),
1155
1798
  output: Checkout,
1156
1799
  errors: RETRY_CHECKOUT_ERROR_MAP,
1157
- middlewares: [sessionMiddleware()]
1800
+ middlewares: [sessionMiddleware({ transitionGuestCart: true })]
1158
1801
  }, async ({ context, input, errors }) => {
1159
1802
  const response = await context.wordpress.woocommerce.store.checkout.processOrder({
1160
1803
  key: input.body.key,
@@ -1162,14 +1805,22 @@ const CHECKOUT_PROCEDURES = {
1162
1805
  payment_data: input.body.paymentData,
1163
1806
  billing_email: input.body.billingEmail,
1164
1807
  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
1808
+ billing_address: serializeCheckoutBillingAddress(input.body.billingAddress),
1809
+ shipping_address: input.body.shippingAddress ? serializeCheckoutShippingAddress(input.body.shippingAddress) : void 0,
1810
+ customer_note: input.body.customerNote,
1811
+ additional_fields: input.body.additionalFields,
1812
+ extensions: input.body.extensions
1167
1813
  }, { headers: context.sessionHeaders });
1168
1814
  if (response.error) switch (response.error.code) {
1815
+ case "rest_invalid_param": throw errors.CHECKOUT_VALIDATION_FAILED({
1816
+ message: response.error.message,
1817
+ data: { fields: validationFields(response.error.data) }
1818
+ });
1169
1819
  case "woocommerce_rest_invalid_billing_email": throw errors.CHECKOUT_EMAIL_INVALID({ message: response.error.message });
1170
1820
  case "woocommerce_rest_checkout_process_payment_error": throw errors.CHECKOUT_PAYMENT_FAILED({ message: response.error.message });
1171
1821
  case "woocommerce_rest_checkout_payment_method_disabled": throw errors.CHECKOUT_PAYMENT_METHOD_DISABLED({ message: response.error.message });
1172
1822
  case "woocommerce_rest_checkout_missing_payment_method": throw errors.CHECKOUT_PAYMENT_METHOD_MISSING({ message: response.error.message });
1823
+ case "woocommerce_rest_checkout_invalid_payment_result": throw errors.CHECKOUT_PAYMENT_RESULT_INVALID({ message: response.error.message });
1173
1824
  case "woocommerce_rest_invalid_user": throw errors.CHECKOUT_ORDER_FORBIDDEN({ message: response.error.message });
1174
1825
  case "woocommerce_rest_invalid_order": throw errors.CHECKOUT_ORDER_NOT_FOUND({ message: response.error.message });
1175
1826
  case "invalid_order_update_status": throw errors.CHECKOUT_ORDER_STATUS_INVALID({ message: response.error.message });
@@ -1183,6 +1834,62 @@ const CHECKOUT_PROCEDURES = {
1183
1834
  return deserializeCheckout(response.data);
1184
1835
  })
1185
1836
  };
1837
+ function validationFields(data) {
1838
+ if (!isRecord(data)) return {};
1839
+ const fields = {};
1840
+ for (const source of [data.params, data.details]) {
1841
+ if (!isRecord(source)) continue;
1842
+ for (const [name, value] of Object.entries(source)) if (typeof value === "string") fields[name] = value;
1843
+ else if (isRecord(value) && typeof value.message === "string") fields[name] = value.message;
1844
+ }
1845
+ return fields;
1846
+ }
1847
+ function conflictData(data) {
1848
+ if (!isRecord(data) || !isRecord(data.cart)) return { cart: null };
1849
+ try {
1850
+ return { cart: deserializeCart(data.cart) };
1851
+ } catch {
1852
+ return { cart: null };
1853
+ }
1854
+ }
1855
+ function isRecord(value) {
1856
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1857
+ }
1858
+
1859
+ //#endregion
1860
+ //#region src/schema.ts
1861
+ const Totals = z$1.object({
1862
+ discountTotal: z$1.number(),
1863
+ discountTaxTotal: z$1.number(),
1864
+ shippingTotal: z$1.number(),
1865
+ shippingTaxTotal: z$1.number(),
1866
+ feeTotal: z$1.number(),
1867
+ feeTaxTotal: z$1.number(),
1868
+ taxTotal: z$1.number(),
1869
+ total: z$1.number()
1870
+ });
1871
+ const ItemTotals = z$1.object({
1872
+ unitPrice: z$1.number(),
1873
+ grossAmount: z$1.number(),
1874
+ discountAmount: z$1.number(),
1875
+ discountTaxAmount: z$1.number(),
1876
+ netAmount: z$1.number(),
1877
+ taxAmount: z$1.number(),
1878
+ total: z$1.number()
1879
+ });
1880
+ const ShippingAddress = z$1.object({
1881
+ firstName: z$1.string(),
1882
+ lastName: z$1.string(),
1883
+ phone: z$1.string(),
1884
+ company: z$1.string().optional(),
1885
+ address1: z$1.string(),
1886
+ address2: z$1.string().optional(),
1887
+ city: z$1.string(),
1888
+ postcode: z$1.string(),
1889
+ state: z$1.string(),
1890
+ country: z$1.string()
1891
+ });
1892
+ const BillingAddress = ShippingAddress.extend({ email: z$1.string() });
1186
1893
 
1187
1894
  //#endregion
1188
1895
  //#region src/customer/schema.ts
@@ -1220,594 +1927,314 @@ function deserializeCustomer(data) {
1220
1927
  address2: data.billing.address_2,
1221
1928
  company: data.billing.company
1222
1929
  },
1223
- shipping: {
1224
- 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()
1930
+ shipping: {
1931
+ firstName: data.shipping.first_name,
1932
+ lastName: data.shipping.last_name,
1933
+ address1: data.shipping.address_1,
1934
+ city: data.shipping.city,
1935
+ country: data.shipping.country,
1936
+ phone: data.shipping.phone,
1937
+ postcode: data.shipping.postcode,
1938
+ state: data.shipping.state,
1939
+ address2: data.shipping.address_2,
1940
+ company: data.shipping.company
1941
+ },
1942
+ email: data.email,
1943
+ firstName: data.first_name,
1944
+ lastName: data.last_name,
1945
+ isPayingCustomer: data.is_paying_customer,
1946
+ meta: toPublicMetadata(data.meta_data),
1947
+ registeredAt: timestampFromWpGmt(data.date_created_gmt) ?? 0,
1948
+ role: data.role,
1949
+ username: data.username
1950
+ };
1951
+ }
1952
+
1953
+ //#endregion
1954
+ //#region src/customer/index.ts
1955
+ const CUSTOMER_PROCEDURES = { get: createProcedure({
1956
+ scope: "api",
1957
+ method: "GET",
1958
+ path: "/customers",
1959
+ output: Customer
1960
+ }, async ({ context, errors }) => {
1961
+ const session = await context.getSession();
1962
+ if (!session) throw errors.FORBIDDEN();
1963
+ const response = await context.wordpress.woocommerce.customers.list({
1964
+ email: session.email,
1965
+ role: "all"
1966
+ });
1967
+ if (response.error) switch (response.error.code) {
1968
+ case "woocommerce_rest_cannot_view": throw errors.FORBIDDEN();
1969
+ default:
1970
+ context.logger.error("Get customer unhandled error", response.error, {
1971
+ email: session.email,
1972
+ code: response.error.code
1973
+ });
1974
+ throw errors.INTERNAL_SERVER_ERROR();
1975
+ }
1976
+ const customer = response.data[0];
1977
+ if (!customer) throw errors.NOT_FOUND();
1978
+ return deserializeCustomer(customer);
1979
+ }) };
1980
+
1981
+ //#endregion
1982
+ //#region src/order/error.ts
1983
+ const GET_ORDER_ERROR_MAP = defineErrorMap({
1984
+ ORDER_NOT_FOUND: {
1985
+ status: 404,
1986
+ message: "Order not found."
1987
+ },
1988
+ ORDER_FORBIDDEN: {
1989
+ status: 403,
1990
+ message: "You are not allowed to view this order."
1991
+ }
1350
1992
  });
1351
- const ProductExtensions = z.record(z.string(), z.unknown());
1352
- const ProductSummary = z.object({
1353
- id: z.number(),
1354
- name: z.string(),
1993
+
1994
+ //#endregion
1995
+ //#region src/order/schema.ts
1996
+ const OrderItemProduct = z.object({
1997
+ sku: z.string().nullable(),
1355
1998
  slug: z.string(),
1356
- parentId: z.number().nullable(),
1357
- type: z.string(),
1358
- variationDescription: z.string(),
1359
1999
  url: z.string().nullable(),
1360
- sku: z.string().nullable(),
1361
2000
  shortDescription: z.string(),
1362
2001
  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
2002
  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 Product = ProductSummary.extend({
1393
- weight: z.string(),
1394
- dimensions: ProductDimensions,
1395
- formattedWeight: z.string(),
1396
- formattedDimensions: z.string(),
1397
- stockQuantity: z.number().nullable(),
1398
- saleStartsAt: z.number().nullable(),
1399
- saleEndsAt: z.number().nullable(),
1400
- seo: Seo.nullable(),
1401
- custom: customFieldsSchema(),
1402
- recommendations: ProductRecommendations.nullable()
1403
- });
1404
- const ProductList = z.object({
1405
- items: z.array(Product),
1406
- meta: ListMetadata
1407
- });
1408
- const RetrieveProductInput = z.object({
1409
- identifier: IdentifierInput,
1410
- previewToken: z.string().optional(),
1411
- recommendations: BooleanLike.optional()
1412
- });
1413
- const PRODUCTS_ORDER_BYS = [
1414
- "date",
1415
- "modified",
1416
- "id",
1417
- "include",
1418
- "title",
1419
- "slug",
1420
- "price",
1421
- "popularity",
1422
- "rating",
1423
- "menu_order",
1424
- "comment_count"
1425
- ];
1426
- const ProductOrderBy = z.enum(PRODUCTS_ORDER_BYS);
1427
- const PRODUCT_DATE_COLUMNS = [
1428
- "date",
1429
- "date_gmt",
1430
- "modified",
1431
- "modified_gmt"
1432
- ];
1433
- const ProductDateColumn = z.enum(PRODUCT_DATE_COLUMNS);
1434
- const PRODUCT_TAXONOMY_OPERATORS = [
1435
- "in",
1436
- "not_in",
1437
- "and"
1438
- ];
1439
- const ProductTaxonomyOperator = z.enum(PRODUCT_TAXONOMY_OPERATORS);
1440
- const PRODUCT_ATTRIBUTE_RELATIONS = ["in", "and"];
1441
- const ProductAttributeRelation = z.enum(PRODUCT_ATTRIBUTE_RELATIONS);
1442
- const PRODUCT_STOCK_STATUSES = [
1443
- "instock",
1444
- "outofstock",
1445
- "onbackorder"
1446
- ];
1447
- const ProductStockStatus = z.enum(PRODUCT_STOCK_STATUSES);
1448
- const PRODUCT_CATALOG_VISIBILITIES = [
1449
- "any",
1450
- "visible",
1451
- "catalog",
1452
- "search",
1453
- "hidden"
1454
- ];
1455
- const ProductCatalogVisibility = z.enum(PRODUCT_CATALOG_VISIBILITIES);
1456
- const PRODUCT_RATINGS = [
1457
- 1,
1458
- 2,
1459
- 3,
1460
- 4,
1461
- 5
1462
- ];
1463
- const ProductRating = z.union([
1464
- z.literal(1),
1465
- z.literal(2),
1466
- z.literal(3),
1467
- z.literal(4),
1468
- z.literal(5)
1469
- ]);
1470
- const ProductRatingInput = NumberLike.pipe(ProductRating);
1471
- const ProductTermIdentifier = z.union([NumberLike, z.string()]);
1472
- const ProductTaxonomyName = z.string().min(1).regex(/^[a-z0-9_-]+$/).refine((name) => !name.endsWith("_operator"));
1473
- const ProductAttributeFilter = z.object({
1474
- taxonomy: z.string(),
1475
- slug: arrayable(z.string()).optional(),
1476
- termId: arrayable(NumberLike).optional(),
1477
- operator: ProductTaxonomyOperator.optional()
1478
- });
1479
- const ProductTaxonomyFilter = z.object({
1480
- taxonomy: ProductTaxonomyName,
1481
- termIds: arrayable(NumberLike).optional(),
1482
- slugs: arrayable(z.string()).optional(),
1483
- operator: ProductTaxonomyOperator.optional()
1484
- }).refine((filter) => filter.termIds === void 0 !== (filter.slugs === void 0), { message: "Provide either termIds or slugs." });
1485
- const ListProductInput = z.object({
1486
- page: NumberLike.optional(),
1487
- perPage: NumberLike.optional(),
1488
- search: z.string().optional(),
1489
- recommendations: BooleanLike.optional(),
1490
- slug: arrayable(z.string()).optional(),
1491
- after: z.string().optional(),
1492
- before: z.string().optional(),
1493
- dateColumn: ProductDateColumn.optional(),
1494
- exclude: arrayable(NumberLike).optional(),
1495
- include: arrayable(NumberLike).optional(),
1496
- offset: NumberLike.optional(),
1497
- order: z.enum(["asc", "desc"]).optional(),
1498
- orderBy: ProductOrderBy.optional(),
1499
- parent: arrayable(NumberLike).optional(),
1500
- parentExclude: arrayable(NumberLike).optional(),
1501
- type: z.string().optional(),
1502
- sku: arrayable(z.string()).optional(),
1503
- featured: BooleanLike.optional(),
1504
- category: arrayable(ProductTermIdentifier).optional(),
1505
- categoryOperator: ProductTaxonomyOperator.optional(),
1506
- brand: arrayable(ProductTermIdentifier).optional(),
1507
- brandOperator: ProductTaxonomyOperator.optional(),
1508
- tag: arrayable(ProductTermIdentifier).optional(),
1509
- tagOperator: ProductTaxonomyOperator.optional(),
1510
- onSale: BooleanLike.optional(),
1511
- minPrice: NumberLike.optional(),
1512
- maxPrice: NumberLike.optional(),
1513
- stockStatus: arrayable(ProductStockStatus).optional(),
1514
- attributes: z.array(ProductAttributeFilter).optional(),
1515
- attributeRelation: ProductAttributeRelation.optional(),
1516
- catalogVisibility: ProductCatalogVisibility.optional(),
1517
- rating: arrayable(ProductRatingInput).optional(),
1518
- related: NumberLike.optional(),
1519
- taxonomies: z.array(ProductTaxonomyFilter).optional()
1520
- });
1521
- const ProductFiltersPriceRange = z.object({
1522
- minPrice: z.number(),
1523
- maxPrice: z.number()
1524
- });
1525
- const ProductFiltersStockStatus = z.object({
1526
- count: z.number(),
1527
- status: ProductStockStatus
2003
+ prices: ProductPrices,
2004
+ custom: ProductCustomFieldsSchema
1528
2005
  });
1529
- const ProductFiltersRatingCount = z.object({
1530
- count: z.number(),
1531
- rating: ProductRating
2006
+ const OrderItem = z.object({
2007
+ id: z.number(),
2008
+ productId: z.number().nullable(),
2009
+ variationId: z.number().nullable(),
2010
+ name: z.string(),
2011
+ quantity: z.number(),
2012
+ selectedAttributes: z.array(CartSelectedAttribute),
2013
+ itemData: z.array(CartItemData),
2014
+ totals: CartItemTotals,
2015
+ product: OrderItemProduct.nullable(),
2016
+ extensions: z.record(z.string(), z.unknown())
1532
2017
  });
1533
- const ProductFiltersTerm = z.object({
2018
+ const OrderFee = z.object({
1534
2019
  id: z.number(),
1535
- parentId: z.number().nullable(),
1536
2020
  name: z.string(),
1537
- slug: z.string(),
1538
- taxonomy: z.string(),
1539
- description: z.string(),
1540
- count: z.number()
2021
+ totals: z.object({
2022
+ total: z.number(),
2023
+ tax: z.number()
2024
+ })
1541
2025
  });
1542
- const ProductFiltersTaxonomyTerm = ProductFiltersTerm.extend({ image: MediaImage.nullable() });
1543
- const ProductFiltersAttributeTerm = ProductFiltersTerm.extend({
1544
- type: SwatchType,
1545
- swatch: z.string().nullable()
2026
+ const OrderTotals = z.object({
2027
+ subtotal: z.number(),
2028
+ itemsTotal: z.number(),
2029
+ itemsTaxTotal: z.number(),
2030
+ feesTotal: z.number(),
2031
+ feesTaxTotal: z.number(),
2032
+ discountTotal: z.number(),
2033
+ discountTaxTotal: z.number(),
2034
+ shippingTotal: z.number().nullable(),
2035
+ shippingTaxTotal: z.number().nullable(),
2036
+ taxTotal: z.number(),
2037
+ refundTotal: z.number(),
2038
+ total: z.number(),
2039
+ taxLines: z.array(CartTaxLine)
1546
2040
  });
1547
- const ProductFilters = z.object({
1548
- priceRange: ProductFiltersPriceRange,
1549
- ratingCounts: z.array(ProductFiltersRatingCount),
1550
- stockStatuses: z.array(ProductFiltersStockStatus),
1551
- attributeTerms: z.array(ProductFiltersAttributeTerm),
1552
- taxonomyTerms: z.array(ProductFiltersTaxonomyTerm),
2041
+ const Order = z.object({
2042
+ id: z.number(),
2043
+ status: z.string(),
2044
+ items: z.array(OrderItem),
2045
+ coupons: z.array(CartCoupon),
2046
+ fees: z.array(OrderFee),
2047
+ billingAddress: CartBillingAddress,
2048
+ shippingAddress: CartShippingAddress,
2049
+ needsPayment: z.boolean(),
2050
+ needsShipping: z.boolean(),
2051
+ paymentRequirements: z.array(z.string()),
2052
+ errors: z.array(CartError),
2053
+ totals: OrderTotals,
1553
2054
  currencyFormat: CurrencyFormat
1554
2055
  });
1555
- const ProductAttributeCount = z.object({
1556
- taxonomy: z.string(),
1557
- operator: z.enum(["or", "and"]).optional()
1558
- });
1559
- const RetrieveProductFiltersInput = ListProductInput.omit({ recommendations: true }).extend({
1560
- ratingCounts: BooleanLike.optional(),
1561
- stockStatusCounts: BooleanLike.optional(),
1562
- taxonomyCounts: z.array(z.string()).optional(),
1563
- attributeCounts: z.array(ProductAttributeCount).optional()
2056
+ const GetOrderInput = z.object({
2057
+ orderId: NumberLike,
2058
+ key: z.string().optional(),
2059
+ billingEmail: z.email().optional()
1564
2060
  });
1565
2061
 
1566
2062
  //#endregion
1567
- //#region src/product/utils.ts
1568
- function assertNoMissingStoreProductInputs() {}
1569
- assertNoMissingStoreProductInputs();
1570
- function deserializeProduct(data) {
1571
- return deserializeStoreProduct(data.kizlo.store_product, null);
1572
- }
1573
- function deserializeStoreProduct(data, recommendations) {
1574
- const summary = deserializeProductSummary(data);
1575
- const { kizlo } = deserializeExtensions(data.extensions);
1576
- return {
1577
- ...summary,
1578
- weight: data.weight,
1579
- dimensions: data.dimensions,
1580
- formattedWeight: data.formatted_weight,
1581
- formattedDimensions: data.formatted_dimensions,
1582
- stockQuantity: nullableNumber(kizlo.stock),
1583
- saleStartsAt: timestampFromIso(typeof kizlo.on_sale_from === "string" ? kizlo.on_sale_from : null),
1584
- saleEndsAt: timestampFromIso(typeof kizlo.on_sale_to === "string" ? kizlo.on_sale_to : null),
1585
- seo: isRecord(kizlo.seo) ? deserializeSeo(kizlo.seo) : null,
1586
- custom: productCustomFields(kizlo.custom),
1587
- recommendations
1588
- };
1589
- }
1590
- function deserializeProductSummary(data) {
1591
- const { extensions, kizlo } = deserializeExtensions(data.extensions);
1592
- const termUrls = deserializeTermUrls(kizlo.term_urls);
2063
+ //#region src/order/utils.ts
2064
+ function assertNoMissing() {}
2065
+ assertNoMissing();
2066
+ assertNoMissing();
2067
+ assertNoMissing();
2068
+ assertNoMissing();
2069
+ assertNoMissing();
2070
+ assertNoMissing();
2071
+ assertNoMissing();
2072
+ assertNoMissing();
2073
+ assertNoMissing();
2074
+ assertNoMissing();
2075
+ assertNoMissing();
2076
+ assertNoMissing();
2077
+ assertNoMissing();
2078
+ assertNoMissing();
2079
+ assertNoMissing();
2080
+ assertNoMissing();
2081
+ function deserializeOrder(data) {
1593
2082
  return {
1594
2083
  id: data.id,
1595
- name: data.name,
1596
- slug: data.slug,
1597
- parentId: data.parent === 0 ? null : data.parent,
1598
- type: data.type,
1599
- variationDescription: data.variation,
1600
- url: typeof kizlo.url === "string" ? kizlo.url : null,
1601
- sku: data.sku === "" ? null : data.sku,
1602
- shortDescription: data.short_description,
1603
- description: data.description,
1604
- isPasswordProtected: data.is_password_protected,
1605
- isOnSale: data.on_sale,
1606
- prices: {
1607
- price: Number(data.prices.price),
1608
- regularPrice: Number(data.prices.regular_price),
1609
- salePrice: data.on_sale ? Number(data.prices.sale_price) : null,
1610
- priceRange: data.prices.price_range ? {
1611
- minAmount: Number(data.prices.price_range.min_amount),
1612
- maxAmount: Number(data.prices.price_range.max_amount)
1613
- } : null
1614
- },
1615
- currencyFormat: deserializeCurrencyFormat(data.prices),
1616
- priceHtml: data.price_html,
1617
- averageRating: Number(data.average_rating),
1618
- reviewCount: data.review_count,
1619
- images: data.images.map((image) => ({
1620
- type: "image",
1621
- id: image.id,
1622
- src: image.src,
1623
- srcset: image.srcset,
1624
- name: image.name,
1625
- alt: image.alt
2084
+ status: data.status,
2085
+ items: data.items.map(deserializeOrderItem),
2086
+ coupons: data.coupons.map((coupon) => ({
2087
+ code: coupon.code,
2088
+ discountType: coupon.discount_type,
2089
+ totals: {
2090
+ discount: Number(coupon.totals.total_discount),
2091
+ discountTax: Number(coupon.totals.total_discount_tax)
2092
+ }
1626
2093
  })),
1627
- categories: data.categories.map((term) => deserializeTermRef(term, "product_cat", termUrls)),
1628
- tags: data.tags.map((term) => deserializeTermRef(term, "product_tag", termUrls)),
1629
- brands: data.brands.map((term) => deserializeTermRef(term, "product_brand", termUrls)),
1630
- attributes: data.attributes.map((attribute) => ({
1631
- id: attribute.id,
1632
- name: attribute.name,
1633
- taxonomy: attribute.taxonomy ?? null,
1634
- hasVariations: attribute.has_variations,
1635
- terms: attribute.terms.map((term) => ({
1636
- id: term.id,
1637
- name: term.name,
1638
- slug: term.slug,
1639
- isDefault: term.default
1640
- }))
2094
+ fees: data.fees.map((fee) => ({
2095
+ id: fee.key,
2096
+ name: fee.name,
2097
+ totals: {
2098
+ total: Number(fee.totals.total),
2099
+ tax: Number(fee.totals.total_tax)
2100
+ }
1641
2101
  })),
1642
- variations: data.variations.map((variation) => ({
1643
- id: variation.id,
1644
- attributes: variation.attributes.map((attribute) => ({
1645
- name: attribute.name,
1646
- value: attribute.value ?? null
2102
+ billingAddress: deserializeCartBillingAddress(data.billing_address),
2103
+ shippingAddress: deserializeCartShippingAddress(data.shipping_address),
2104
+ needsPayment: data.needs_payment,
2105
+ needsShipping: data.needs_shipping,
2106
+ paymentRequirements: data.payment_requirements,
2107
+ errors: data.errors,
2108
+ totals: {
2109
+ subtotal: Number(data.totals.subtotal),
2110
+ itemsTotal: Number(data.totals.total_items),
2111
+ itemsTaxTotal: Number(data.totals.total_items_tax),
2112
+ feesTotal: Number(data.totals.total_fees),
2113
+ feesTaxTotal: Number(data.totals.total_fees_tax),
2114
+ discountTotal: Number(data.totals.total_discount),
2115
+ discountTaxTotal: Number(data.totals.total_discount_tax),
2116
+ shippingTotal: nullableMoney(data.totals.total_shipping),
2117
+ shippingTaxTotal: nullableMoney(data.totals.total_shipping_tax),
2118
+ taxTotal: Number(data.totals.total_tax),
2119
+ refundTotal: Number(data.totals.total_refund),
2120
+ total: Number(data.totals.total_price),
2121
+ taxLines: data.totals.tax_lines.map((line) => ({
2122
+ name: line.name,
2123
+ price: Number(line.price),
2124
+ rate: line.rate
1647
2125
  }))
1648
- })),
1649
- groupedProductIds: data.grouped_products,
1650
- hasOptions: data.has_options,
1651
- isPurchasable: data.is_purchasable,
1652
- isInStock: data.is_in_stock,
1653
- isOnBackorder: data.is_on_backorder,
1654
- stockAvailability: data.stock_availability,
1655
- lowStockRemaining: data.low_stock_remaining,
1656
- isSoldIndividually: data.sold_individually,
1657
- addToCart: {
1658
- text: data.add_to_cart.text,
1659
- description: data.add_to_cart.description,
1660
- singleText: data.add_to_cart.single_text,
1661
- minimum: data.add_to_cart.minimum,
1662
- maximum: data.add_to_cart.maximum,
1663
- multipleOf: data.add_to_cart.multiple_of
1664
2126
  },
1665
- extensions
1666
- };
1667
- }
1668
- function deserializeProductRecommendations(data) {
1669
- return {
1670
- upsells: deserializeEmbeddedProducts(data._embedded?.upsells),
1671
- crossSells: deserializeEmbeddedProducts(data._embedded?.cross_sells),
1672
- related: deserializeEmbeddedProducts(data._embedded?.related)
2127
+ currencyFormat: deserializeCurrencyFormat(data.totals)
1673
2128
  };
1674
2129
  }
1675
- function productCustomFields(value) {
1676
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
1677
- }
1678
- function deserializeProductFilters(data) {
1679
- if (!data.price_range) return null;
1680
- const maxPrice = +data.price_range.max_price;
1681
- const minPrice = +data.price_range.min_price;
2130
+ function deserializeOrderItem(item) {
2131
+ const { extensions, kizlo } = deserializeExtensions(item.extensions);
2132
+ const productId = nullableId(kizlo.product_id);
2133
+ const variationId = nullableId(kizlo.variation_id);
1682
2134
  return {
1683
- ratingCounts: (data.rating_counts ?? []).flatMap((entry) => PRODUCT_RATINGS.includes(entry.rating) ? [{
1684
- count: entry.count,
1685
- rating: entry.rating
1686
- }] : []),
1687
- stockStatuses: (data.stock_status_counts ?? []).flatMap((entry) => {
1688
- const status = stockStatus(entry.status);
1689
- return status ? [{
1690
- count: entry.count,
1691
- status
1692
- }] : [];
1693
- }),
1694
- taxonomyTerms: data.kizlo.taxonomy_counts.map((item) => ({
1695
- id: item.id,
1696
- name: item.name,
1697
- count: item.count,
1698
- description: item.description,
1699
- parentId: item.parent,
1700
- slug: item.slug,
1701
- taxonomy: item.taxonomy,
1702
- image: item.image
2135
+ id: item.id,
2136
+ productId,
2137
+ variationId,
2138
+ name: item.name,
2139
+ quantity: item.quantity,
2140
+ selectedAttributes: item.variation.map((attribute) => ({
2141
+ name: attribute.attribute,
2142
+ attribute: attribute.raw_attribute,
2143
+ value: attribute.value
1703
2144
  })),
1704
- attributeTerms: data.kizlo.attribute_counts.map((item) => ({
1705
- id: item.id,
1706
- name: item.name,
1707
- count: item.count,
1708
- description: item.description,
1709
- parentId: item.parent,
1710
- slug: item.slug,
1711
- swatch: item.swatch,
1712
- type: item.swatch_type,
1713
- taxonomy: item.taxonomy
2145
+ itemData: item.item_data.map((entry) => ({
2146
+ name: entry.name,
2147
+ value: entry.value,
2148
+ display: entry.display
1714
2149
  })),
1715
- priceRange: {
1716
- maxPrice,
1717
- minPrice
2150
+ totals: {
2151
+ subtotal: Number(item.totals.line_subtotal),
2152
+ subtotalTax: Number(item.totals.line_subtotal_tax),
2153
+ total: Number(item.totals.line_total),
2154
+ totalTax: Number(item.totals.line_total_tax)
1718
2155
  },
1719
- currencyFormat: deserializeCurrencyFormat(data.price_range)
1720
- };
1721
- }
1722
- function serializeProductListInput(data) {
1723
- const searchParams = {
1724
- after: data?.after,
1725
- attribute_relation: data?.attributeRelation,
1726
- before: data?.before,
1727
- brand: commaSeparated(data?.brand),
1728
- brand_operator: data?.brandOperator,
1729
- catalog_visibility: data?.catalogVisibility,
1730
- category: commaSeparated(data?.category),
1731
- category_operator: data?.categoryOperator,
1732
- date_column: data?.dateColumn,
1733
- featured: data?.featured,
1734
- max_price: data?.maxPrice === void 0 ? void 0 : String(data.maxPrice),
1735
- min_price: data?.minPrice === void 0 ? void 0 : String(data.minPrice),
1736
- on_sale: data?.onSale,
1737
- orderby: data?.orderBy,
1738
- parent: normalizeArrayableValue(data?.parent),
1739
- parent_exclude: normalizeArrayableValue(data?.parentExclude),
1740
- rating: normalizeArrayableValue(data?.rating),
1741
- sku: commaSeparated(data?.sku),
1742
- slug: commaSeparated(data?.slug),
1743
- stock_status: normalizeArrayableValue(data?.stockStatus),
1744
- tag: commaSeparated(data?.tag),
1745
- tag_operator: data?.tagOperator,
1746
- type: data?.type,
1747
- attributes: data?.attributes?.map((item) => ({
1748
- operator: item.operator,
1749
- attribute: item.taxonomy,
1750
- slug: normalizeArrayableValue(item.slug),
1751
- term_id: normalizeArrayableValue(item.termId)
1752
- })),
1753
- exclude: normalizeArrayableValue(data?.exclude),
1754
- include: normalizeArrayableValue(data?.include),
1755
- offset: data?.offset,
1756
- order: data?.order,
1757
- page: data?.page,
1758
- per_page: data?.perPage,
1759
- related: data?.related,
1760
- search: data?.search
1761
- };
1762
- for (const filter of data?.taxonomies ?? []) {
1763
- const key = `_unstable_tax_${filter.taxonomy}`;
1764
- searchParams[key] = commaSeparated(filter.termIds ?? filter.slugs);
1765
- if (filter.operator !== void 0) searchParams[`${key}_operator`] = filter.operator;
1766
- }
1767
- return searchParams;
1768
- }
1769
- function commaSeparated(value) {
1770
- if (value === void 0) return void 0;
1771
- return (Array.isArray(value) ? value : [value]).join(",");
1772
- }
1773
- function stockStatus(status) {
1774
- return PRODUCT_STOCK_STATUSES.includes(status) ? status : null;
1775
- }
1776
- function deserializeTermRef(term, taxonomy, urls) {
1777
- return {
1778
- id: term.id,
1779
- name: term.name,
1780
- slug: term.slug,
1781
- url: urls[`${taxonomy}:${term.id}`] ?? null
2156
+ product: kizlo.product_exists === true ? deserializeOrderItemProduct(item, kizlo) : null,
2157
+ extensions
1782
2158
  };
1783
2159
  }
1784
- function deserializeTermUrls(value) {
1785
- if (!Array.isArray(value)) return {};
1786
- return Object.fromEntries(value.flatMap((item) => {
1787
- if (!isRecord(item) || typeof item.id !== "number" || typeof item.taxonomy !== "string" || typeof item.url !== "string") return [];
1788
- return [[`${item.taxonomy}:${item.id}`, item.url]];
1789
- }));
1790
- }
1791
- function deserializeEmbeddedProducts(collections) {
1792
- return (collections ?? []).flat().map(deserializeProductSummary);
1793
- }
1794
- function deserializeExtensions(value) {
1795
- const { kizlo: rawKizlo,...extensions } = asRecord(value);
2160
+ function deserializeOrderItemProduct(item, kizlo) {
1796
2161
  return {
1797
- extensions,
1798
- kizlo: asRecord(rawKizlo)
2162
+ sku: item.sku === "" ? null : item.sku,
2163
+ slug: typeof kizlo.slug === "string" ? kizlo.slug : "",
2164
+ url: typeof kizlo.url === "string" ? kizlo.url : null,
2165
+ shortDescription: item.short_description,
2166
+ description: item.description,
2167
+ images: item.images.map((image) => ({
2168
+ type: "image",
2169
+ id: image.id,
2170
+ name: image.name,
2171
+ alt: image.alt,
2172
+ src: image.src,
2173
+ srcset: image.srcset
2174
+ })),
2175
+ prices: {
2176
+ price: Number(item.prices.price),
2177
+ regularPrice: Number(item.prices.regular_price),
2178
+ salePrice: item.prices.sale_price === "" || item.prices.sale_price === item.prices.regular_price ? null : Number(item.prices.sale_price),
2179
+ priceRange: item.prices.price_range ? {
2180
+ minAmount: Number(item.prices.price_range.min_amount),
2181
+ maxAmount: Number(item.prices.price_range.max_amount)
2182
+ } : null
2183
+ },
2184
+ custom: productCustomFields(kizlo.custom)
1799
2185
  };
1800
2186
  }
1801
- function asRecord(value) {
1802
- return isRecord(value) ? value : {};
1803
- }
1804
- function isRecord(value) {
1805
- return typeof value === "object" && value !== null && !Array.isArray(value);
2187
+ function nullableId(value) {
2188
+ return typeof value === "number" && value !== 0 ? value : null;
1806
2189
  }
1807
- function nullableNumber(value) {
1808
- return typeof value === "number" ? value : null;
2190
+ function nullableMoney(value) {
2191
+ return value === null ? null : Number(value);
1809
2192
  }
1810
2193
 
2194
+ //#endregion
2195
+ //#region src/order/index.ts
2196
+ const ORDER_PROCEDURES = { get: createProcedure({
2197
+ scope: "api",
2198
+ method: "GET",
2199
+ path: "/orders/{orderId}",
2200
+ params: GetOrderInput.pick({ orderId: true }),
2201
+ query: GetOrderInput.pick({
2202
+ key: true,
2203
+ billingEmail: true
2204
+ }).optional(),
2205
+ output: schemaType(Order),
2206
+ errors: GET_ORDER_ERROR_MAP,
2207
+ middlewares: [sessionMiddleware()]
2208
+ }, async ({ context, input, errors }) => {
2209
+ const response = await context.wordpress.woocommerce.store.orders.get({
2210
+ id: input.params.orderId,
2211
+ key: input.query?.key,
2212
+ billing_email: input.query?.billingEmail
2213
+ }, { headers: context.sessionHeaders });
2214
+ if (response.error) switch (response.error.code) {
2215
+ case "woocommerce_rest_invalid_order":
2216
+ if (response.status === 404) throw errors.ORDER_NOT_FOUND({ message: response.error.message });
2217
+ throw errors.ORDER_FORBIDDEN({ message: response.error.message });
2218
+ case "woocommerce_rest_invalid_billing_email":
2219
+ case "woocommerce_rest_invalid_user": throw errors.ORDER_FORBIDDEN({ message: response.error.message });
2220
+ default:
2221
+ context.logger.error("Get order unhandled error", response.error, {
2222
+ orderId: input.params.orderId,
2223
+ code: response.error.code
2224
+ });
2225
+ throw errors.INTERNAL_SERVER_ERROR();
2226
+ }
2227
+ return deserializeOrder(response.data);
2228
+ }) };
2229
+
2230
+ //#endregion
2231
+ //#region src/product/error.ts
2232
+ const GET_PRODUCT_ERROR_MAP = defineErrorMap({ PRODUCT_NOT_FOUND: {
2233
+ status: 404,
2234
+ message: "Product not found."
2235
+ } });
2236
+ const LIST_PRODUCT_ERROR_MAP = defineErrorMap({});
2237
+
1811
2238
  //#endregion
1812
2239
  //#region src/product/index.ts
1813
2240
  const PRODUCT_EMBEDS = "upsells,cross_sells,related";
@@ -1821,7 +2248,7 @@ const PRODUCT_PROCEDURES = {
1821
2248
  previewToken: true,
1822
2249
  recommendations: true
1823
2250
  }).optional(),
1824
- output: Product,
2251
+ output: schemaType(Product),
1825
2252
  errors: GET_PRODUCT_ERROR_MAP
1826
2253
  }, async ({ context, input, errors }) => {
1827
2254
  if (input.query?.previewToken) {
@@ -1866,7 +2293,7 @@ const PRODUCT_PROCEDURES = {
1866
2293
  method: "GET",
1867
2294
  path: "/products",
1868
2295
  query: ListProductInput.optional(),
1869
- output: ProductList,
2296
+ output: schemaType(ProductList),
1870
2297
  errors: LIST_PRODUCT_ERROR_MAP
1871
2298
  }, async ({ context, input, errors }) => {
1872
2299
  const searchParams = serializeProductListInput(input.query);
@@ -1927,14 +2354,14 @@ function woocommerce() {
1927
2354
  requires: {
1928
2355
  plugins: [{
1929
2356
  name: "kizlo-woocommerce",
1930
- version: "0.3.0"
2357
+ version: "0.4.0"
1931
2358
  }],
1932
2359
  endpoints: [
1933
2360
  "woocommerce.customers",
1934
2361
  "woocommerce.products",
1935
- "woocommerce.kizlo.cart",
1936
2362
  "woocommerce.store.cart",
1937
2363
  "woocommerce.store.checkout",
2364
+ "woocommerce.store.orders",
1938
2365
  "woocommerce.store.products"
1939
2366
  ]
1940
2367
  },
@@ -1942,10 +2369,11 @@ function woocommerce() {
1942
2369
  cart: CART_PROCEDURES,
1943
2370
  products: PRODUCT_PROCEDURES,
1944
2371
  checkout: CHECKOUT_PROCEDURES,
1945
- customers: CUSTOMER_PROCEDURES
2372
+ customers: CUSTOMER_PROCEDURES,
2373
+ orders: ORDER_PROCEDURES
1946
2374
  }
1947
2375
  });
1948
2376
  }
1949
2377
 
1950
2378
  //#endregion
1951
- 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, 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 };
2379
+ export { AddCartItemInput, ApplyCouponInput, Cart, CartAdditionalFields, CartBillingAddress, CartCoupon, CartCouponTotals, CartError, CartFee, CartFeeTotals, CartItem, CartItemData, CartItemQuantityLimits, CartItemTotals, 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 };