@kizlo/woocommerce 0.2.2 → 0.3.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,5 +1,5 @@
1
- import { CurrencyFormat, IdentifierInput, KizloError, ListMetadata, Seo, createIntegration, createMiddleware, createProcedure, defineErrorMap, deserializeCurrencyFormat, deserializeListMetadata } from "kizlo";
2
- import { BooleanLike, Media, Metadata, NumberLike, arrayable, normalizeArrayableValue, random, seconds, timestampSec, toPublicMetadata, tryCatch } from "@kizlo/shared";
1
+ import { CurrencyFormat, IdentifierInput, KizloError, ListMetadata, Seo, createIntegration, createMiddleware, createProcedure, customFieldsSchema, defineErrorMap, deserializeCurrencyFormat, deserializeListMetadata, deserializeSeo } from "kizlo";
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";
5
5
  import z$2 from "zod";
@@ -190,225 +190,6 @@ const REMOVE_COUPON_ERROR_MAP = defineErrorMap({
190
190
  }
191
191
  });
192
192
 
193
- //#endregion
194
- //#region src/product/schema.ts
195
- const SWATCH_TYPES = [
196
- "text",
197
- "color",
198
- "image"
199
- ];
200
- const SwatchType = z.enum(SWATCH_TYPES);
201
- const PRODUCT_TYPES = [
202
- "simple",
203
- "grouped",
204
- "external",
205
- "variable",
206
- "variation"
207
- ];
208
- const ProductType = z.enum(PRODUCT_TYPES);
209
- const ProductTagRef = z.object({
210
- id: z.number(),
211
- name: z.string(),
212
- slug: z.string()
213
- });
214
- const ProductBrandRef = z.object({
215
- id: z.number(),
216
- name: z.string(),
217
- slug: z.string()
218
- });
219
- const ProductAttributeTermRef = z.object({
220
- id: z.number(),
221
- name: z.string(),
222
- slug: z.string()
223
- });
224
- const ProductAttributeRef = z.object({
225
- id: z.number(),
226
- name: z.string(),
227
- taxonomy: z.string().nullable(),
228
- hasVariations: z.boolean(),
229
- terms: z.array(ProductAttributeTermRef)
230
- });
231
- const ProductCategoryRef = z.object({
232
- id: z.number(),
233
- name: z.string(),
234
- slug: z.string()
235
- });
236
- const ProductVariationAttributeRef = z.object({
237
- name: z.string(),
238
- value: z.string()
239
- });
240
- const ProductVariationRef = z.object({
241
- id: z.number(),
242
- attributes: z.array(ProductVariationAttributeRef)
243
- });
244
- const ProductPrices = z.object({
245
- price: z.number(),
246
- salePrice: z.number().nullable(),
247
- regularPrice: z.number()
248
- });
249
- const Product = z.object({
250
- id: z.number(),
251
- parentId: z.number(),
252
- type: ProductType,
253
- name: z.string(),
254
- slug: z.string(),
255
- sku: z.string().nullable(),
256
- shortDescription: z.string(),
257
- description: z.string(),
258
- onSaleFrom: z.number().nullable(),
259
- onSaleTo: z.number().nullable(),
260
- isOnSale: z.boolean(),
261
- prices: ProductPrices,
262
- averageRating: z.string(),
263
- reviewCount: z.number(),
264
- images: z.array(Media),
265
- categories: z.array(ProductCategoryRef),
266
- tags: z.array(ProductTagRef),
267
- brands: z.array(ProductBrandRef),
268
- attributes: z.array(ProductAttributeRef),
269
- variations: z.array(ProductVariationRef),
270
- groupedProducts: z.array(z.number()),
271
- isSoldIndividually: z.boolean(),
272
- hasOptions: z.boolean(),
273
- isPurchasable: z.boolean(),
274
- isInStock: z.boolean(),
275
- isOnBackorder: z.boolean(),
276
- stock: z.number().nullable(),
277
- lowStockRemaining: z.number().nullable(),
278
- seo: Seo.nullable(),
279
- currencyFormat: CurrencyFormat,
280
- meta: Metadata
281
- });
282
- const ProductList = z.object({
283
- items: z.array(Product),
284
- meta: ListMetadata
285
- });
286
- const RetrieveProductInput = z.object({
287
- identifier: IdentifierInput,
288
- previewToken: z.string().optional()
289
- });
290
- const PRODUCTS_ORDER_BYS = [
291
- "date",
292
- "id",
293
- "include",
294
- "title",
295
- "slug",
296
- "price",
297
- "popularity",
298
- "rating",
299
- "menu_order",
300
- "comment_count"
301
- ];
302
- const ProductOrderBy = z.enum(PRODUCTS_ORDER_BYS);
303
- const PRODUCT_TAXONOMY_OPERATORS = [
304
- "in",
305
- "not_in",
306
- "and"
307
- ];
308
- const ProductTaxonomyOperator = z.enum(PRODUCT_TAXONOMY_OPERATORS);
309
- const PRODUCT_ATTRIBUTE_RELATIONS = ["in", "and"];
310
- const ProductAttributeRelation = z.enum(PRODUCT_ATTRIBUTE_RELATIONS);
311
- const PRODUCT_STOCK_STATUSES = [
312
- "instock",
313
- "outofstock",
314
- "onbackorder"
315
- ];
316
- const ProductStockStatus = z.enum(PRODUCT_STOCK_STATUSES);
317
- const PRODUCT_CATALOG_VISIBILITIES = [
318
- "any",
319
- "visible",
320
- "catalog",
321
- "search",
322
- "hidden"
323
- ];
324
- const ProductCatalogVisibility = z.enum(PRODUCT_CATALOG_VISIBILITIES);
325
- const PRODUCT_RATINGS = [
326
- "1",
327
- "2",
328
- "3",
329
- "4",
330
- "5"
331
- ];
332
- const ProductRating = z.enum(PRODUCT_RATINGS);
333
- const ProductAttributeFilter = z.object({
334
- attribute: z.string().optional(),
335
- slug: arrayable(z.string()).optional(),
336
- termId: arrayable(NumberLike).optional(),
337
- operator: ProductTaxonomyOperator.optional()
338
- });
339
- const ListProductInput = z.object({
340
- page: NumberLike.optional(),
341
- perPage: NumberLike.optional(),
342
- search: z.string().optional(),
343
- slug: z.string().optional(),
344
- after: z.string().optional(),
345
- before: z.string().optional(),
346
- exclude: arrayable(NumberLike).optional(),
347
- include: arrayable(NumberLike).optional(),
348
- offset: NumberLike.optional(),
349
- order: z.enum(["asc", "desc"]).optional(),
350
- orderby: ProductOrderBy.optional(),
351
- parent: arrayable(NumberLike).optional(),
352
- parentExclude: arrayable(NumberLike).optional(),
353
- type: ProductType.optional(),
354
- sku: z.string().optional(),
355
- featured: BooleanLike.optional(),
356
- category: z.string().optional(),
357
- categoryOperator: ProductTaxonomyOperator.optional(),
358
- brand: z.string().optional(),
359
- brandOperator: ProductTaxonomyOperator.optional(),
360
- tag: z.string().optional(),
361
- tagOperator: ProductTaxonomyOperator.optional(),
362
- onSale: BooleanLike.optional(),
363
- minPrice: z.string().optional(),
364
- maxPrice: z.string().optional(),
365
- stockStatus: arrayable(ProductStockStatus).optional(),
366
- attributes: z.array(ProductAttributeFilter).optional(),
367
- attributeRelation: ProductAttributeRelation.optional(),
368
- catalogVisibility: ProductCatalogVisibility.optional(),
369
- rating: arrayable(ProductRating).optional(),
370
- related: NumberLike.optional()
371
- });
372
- const ProductFiltersPriceRange = z.object({
373
- minPrice: z.number(),
374
- maxPrice: z.number()
375
- });
376
- const ProductFiltersStockStatus = z.object({
377
- count: z.number(),
378
- status: ProductStockStatus
379
- });
380
- const ProductFiltersTerm = z.object({
381
- id: z.number(),
382
- parentId: z.number().nullable(),
383
- name: z.string(),
384
- slug: z.string(),
385
- taxonomy: z.string(),
386
- description: z.string(),
387
- count: z.number()
388
- });
389
- const ProductFiltersTaxonomyTerm = ProductFiltersTerm.extend({ image: Media.nullable() });
390
- const ProductFiltersAttributeTerm = ProductFiltersTerm.extend({
391
- type: SwatchType,
392
- swatch: z.string().nullable()
393
- });
394
- const ProductFilters = z.object({
395
- priceRange: ProductFiltersPriceRange,
396
- stockStatuses: z.array(ProductFiltersStockStatus),
397
- attributeTerms: z.array(ProductFiltersAttributeTerm),
398
- taxonomyTerms: z.array(ProductFiltersTaxonomyTerm),
399
- currencyFormat: CurrencyFormat
400
- });
401
- const CalculateAttributeFilter = z.object({
402
- taxonomy: z.string(),
403
- queryType: z.enum(["or", "and"]).optional()
404
- });
405
- const RetrieveProductFiltersInput = ListProductInput.extend({
406
- ratingFilters: BooleanLike.optional(),
407
- stockStatusFilters: BooleanLike.optional(),
408
- taxonomyFilters: z.array(z.string()).optional(),
409
- attributeFilters: z.array(CalculateAttributeFilter).optional()
410
- });
411
-
412
193
  //#endregion
413
194
  //#region src/schema.ts
414
195
  const Totals = z$1.object({
@@ -490,6 +271,11 @@ const CartItemVariation = z$1.object({
490
271
  attribute: z$1.string(),
491
272
  value: z$1.string()
492
273
  });
274
+ const CartItemPrices = z$1.object({
275
+ price: z$1.number(),
276
+ salePrice: z$1.number().nullable(),
277
+ regularPrice: z$1.number()
278
+ });
493
279
  const CartItem = z$1.object({
494
280
  key: z$1.string(),
495
281
  type: z$1.string(),
@@ -503,9 +289,9 @@ const CartItem = z$1.object({
503
289
  slug: z$1.string(),
504
290
  lowStockCount: z$1.number().nullable(),
505
291
  isSoldIndividually: z$1.boolean(),
506
- images: z$1.array(Media),
292
+ images: z$1.array(MediaImage),
507
293
  variations: z$1.array(CartItemVariation),
508
- prices: ProductPrices,
294
+ prices: CartItemPrices,
509
295
  quantity: z$1.number(),
510
296
  totals: ItemTotals
511
297
  });
@@ -596,6 +382,7 @@ function deserializeCart(data) {
596
382
  quantity: item.quantity,
597
383
  isSoldIndividually: item.sold_individually,
598
384
  images: item.images.map((item$1) => ({
385
+ type: "image",
599
386
  id: item$1.id,
600
387
  name: item$1.name,
601
388
  alt: item$1.alt,
@@ -1450,7 +1237,7 @@ function deserializeCustomer(data) {
1450
1237
  lastName: data.last_name,
1451
1238
  isPayingCustomer: data.is_paying_customer,
1452
1239
  meta: toPublicMetadata(data.meta_data),
1453
- registeredAt: new Date(data.date_created).getTime(),
1240
+ registeredAt: timestampFromWpGmt(data.date_created_gmt) ?? 0,
1454
1241
  role: data.role,
1455
1242
  username: data.username
1456
1243
  };
@@ -1488,109 +1275,415 @@ const GET_PRODUCT_ERROR_MAP = defineErrorMap({ PRODUCT_NOT_FOUND: {
1488
1275
  } });
1489
1276
  const LIST_PRODUCT_ERROR_MAP = defineErrorMap({});
1490
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()
1350
+ });
1351
+ const ProductExtensions = z.record(z.string(), z.unknown());
1352
+ const ProductSummary = z.object({
1353
+ id: z.number(),
1354
+ name: z.string(),
1355
+ slug: z.string(),
1356
+ parentId: z.number().nullable(),
1357
+ type: z.string(),
1358
+ variationDescription: z.string(),
1359
+ url: z.string().nullable(),
1360
+ sku: z.string().nullable(),
1361
+ shortDescription: z.string(),
1362
+ 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
+ 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
1528
+ });
1529
+ const ProductFiltersRatingCount = z.object({
1530
+ count: z.number(),
1531
+ rating: ProductRating
1532
+ });
1533
+ const ProductFiltersTerm = z.object({
1534
+ id: z.number(),
1535
+ parentId: z.number().nullable(),
1536
+ name: z.string(),
1537
+ slug: z.string(),
1538
+ taxonomy: z.string(),
1539
+ description: z.string(),
1540
+ count: z.number()
1541
+ });
1542
+ const ProductFiltersTaxonomyTerm = ProductFiltersTerm.extend({ image: MediaImage.nullable() });
1543
+ const ProductFiltersAttributeTerm = ProductFiltersTerm.extend({
1544
+ type: SwatchType,
1545
+ swatch: z.string().nullable()
1546
+ });
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),
1553
+ currencyFormat: CurrencyFormat
1554
+ });
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()
1564
+ });
1565
+
1491
1566
  //#endregion
1492
1567
  //#region src/product/utils.ts
1568
+ function assertNoMissingStoreProductInputs() {}
1569
+ assertNoMissingStoreProductInputs();
1493
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);
1494
1576
  return {
1495
- id: data.id,
1496
- type: data.type,
1497
- slug: data.slug,
1498
- name: data.name,
1499
- sku: data.sku,
1500
- description: data.description,
1501
- shortDescription: data.short_description,
1502
- prices: {
1503
- price: data.kizlo.prices.price,
1504
- salePrice: data.kizlo.prices.sale_price,
1505
- regularPrice: data.kizlo.prices.regular_price
1506
- },
1507
- isSoldIndividually: data.sold_individually,
1508
- onSaleFrom: data.date_on_sale_from ? toTimestamp(data.date_on_sale_from) : null,
1509
- lowStockRemaining: data.low_stock_amount,
1510
- onSaleTo: data.date_on_sale_to ? toTimestamp(data.date_on_sale_to) : null,
1511
- isInStock: data.stock_status === "instock",
1512
- stock: data.stock_quantity,
1513
- images: deserializeImages(data.images),
1514
- categories: deserializeTermRefs(data.categories),
1515
- tags: deserializeTermRefs(data.tags),
1516
- averageRating: data.average_rating,
1517
- reviewCount: data.rating_count,
1518
- attributes: data.kizlo.attributes.map((item) => ({
1519
- id: item.id,
1520
- hasVariations: item.has_variations,
1521
- name: item.name,
1522
- taxonomy: item.taxonomy,
1523
- terms: item.terms
1524
- })),
1525
- groupedProducts: data.grouped_products,
1526
- isOnBackorder: data.backordered,
1527
- isPurchasable: data.purchasable,
1528
- isOnSale: data.on_sale,
1529
- parentId: data.parent_id,
1530
- brands: data.brands,
1531
- variations: data.kizlo.variations,
1532
- hasOptions: !!data.kizlo.variations.length,
1533
- currencyFormat: deserializeCurrencyFormat(data.kizlo.currency_format),
1534
- meta: toPublicMetadata(data.meta_data),
1535
- seo: null
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
1536
1588
  };
1537
1589
  }
1538
- function deserializeStoreProduct(data) {
1539
- const kizlo = data.extensions.kizlo ?? {
1540
- stock: null,
1541
- on_sale_from: null,
1542
- on_sale_to: null,
1543
- hs_code: null
1544
- };
1590
+ function deserializeProductSummary(data) {
1591
+ const { extensions, kizlo } = deserializeExtensions(data.extensions);
1592
+ const termUrls = deserializeTermUrls(kizlo.term_urls);
1545
1593
  return {
1546
1594
  id: data.id,
1547
- type: productType(data.type),
1548
1595
  name: data.name,
1549
1596
  slug: data.slug,
1550
- sku: data.sku,
1551
- description: data.description,
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,
1552
1602
  shortDescription: data.short_description,
1553
- isInStock: data.is_in_stock,
1554
- reviewCount: data.review_count,
1555
- averageRating: data.average_rating,
1556
- lowStockRemaining: data.low_stock_remaining,
1557
- isSoldIndividually: data.sold_individually,
1603
+ description: data.description,
1604
+ isPasswordProtected: data.is_password_protected,
1605
+ isOnSale: data.on_sale,
1558
1606
  prices: {
1559
- price: +data.prices.price,
1560
- salePrice: +data.prices.sale_price,
1561
- regularPrice: +data.prices.regular_price
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
1562
1614
  },
1563
- images: deserializeImages(data.images),
1564
- categories: deserializeTermRefs(data.categories),
1565
- tags: deserializeTermRefs(data.tags),
1566
- attributes: data.attributes.map((item) => ({
1567
- id: item.id,
1568
- name: item.name,
1569
- terms: item.terms,
1570
- taxonomy: item.taxonomy,
1571
- hasVariations: item.has_variations
1572
- })),
1573
1615
  currencyFormat: deserializeCurrencyFormat(data.prices),
1574
- brands: data.brands,
1575
- groupedProducts: data.grouped_products,
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
1626
+ })),
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
+ }))
1641
+ })),
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
1647
+ }))
1648
+ })),
1649
+ groupedProductIds: data.grouped_products,
1576
1650
  hasOptions: data.has_options,
1577
- isOnBackorder: data.is_on_backorder,
1578
1651
  isPurchasable: data.is_purchasable,
1579
- isOnSale: data.on_sale,
1580
- parentId: data.parent,
1581
- variations: data.variations,
1582
- stock: kizlo.stock,
1583
- onSaleFrom: kizlo.on_sale_from ? toTimestamp(kizlo.on_sale_from) : null,
1584
- onSaleTo: kizlo.on_sale_to ? toTimestamp(kizlo.on_sale_to) : null,
1585
- seo: null,
1586
- meta: {}
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
+ },
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)
1587
1673
  };
1588
1674
  }
1675
+ function productCustomFields(value) {
1676
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
1677
+ }
1589
1678
  function deserializeProductFilters(data) {
1590
1679
  if (!data.price_range) return null;
1591
1680
  const maxPrice = +data.price_range.max_price;
1592
1681
  const minPrice = +data.price_range.min_price;
1593
1682
  return {
1683
+ ratingCounts: (data.rating_counts ?? []).flatMap((entry) => PRODUCT_RATINGS.includes(entry.rating) ? [{
1684
+ count: entry.count,
1685
+ rating: entry.rating
1686
+ }] : []),
1594
1687
  stockStatuses: (data.stock_status_counts ?? []).flatMap((entry) => {
1595
1688
  const status = stockStatus(entry.status);
1596
1689
  return status ? [{
@@ -1606,12 +1699,7 @@ function deserializeProductFilters(data) {
1606
1699
  parentId: item.parent,
1607
1700
  slug: item.slug,
1608
1701
  taxonomy: item.taxonomy,
1609
- image: item.thumbnail ? {
1610
- id: 0,
1611
- alt: item.name,
1612
- name: item.name,
1613
- src: item.thumbnail
1614
- } : null
1702
+ image: item.image
1615
1703
  })),
1616
1704
  attributeTerms: data.kizlo.attribute_counts.map((item) => ({
1617
1705
  id: item.id,
@@ -1632,32 +1720,33 @@ function deserializeProductFilters(data) {
1632
1720
  };
1633
1721
  }
1634
1722
  function serializeProductListInput(data) {
1635
- return {
1723
+ const searchParams = {
1636
1724
  after: data?.after,
1637
1725
  attribute_relation: data?.attributeRelation,
1638
1726
  before: data?.before,
1639
- brand: data?.brand,
1727
+ brand: commaSeparated(data?.brand),
1640
1728
  brand_operator: data?.brandOperator,
1641
1729
  catalog_visibility: data?.catalogVisibility,
1642
- category: data?.category,
1730
+ category: commaSeparated(data?.category),
1643
1731
  category_operator: data?.categoryOperator,
1732
+ date_column: data?.dateColumn,
1644
1733
  featured: data?.featured,
1645
- max_price: data?.maxPrice ? String(data?.maxPrice) : void 0,
1646
- min_price: data?.minPrice ? String(data?.minPrice) : void 0,
1734
+ max_price: data?.maxPrice === void 0 ? void 0 : String(data.maxPrice),
1735
+ min_price: data?.minPrice === void 0 ? void 0 : String(data.minPrice),
1647
1736
  on_sale: data?.onSale,
1648
- orderby: data?.orderby,
1737
+ orderby: data?.orderBy,
1649
1738
  parent: normalizeArrayableValue(data?.parent),
1650
1739
  parent_exclude: normalizeArrayableValue(data?.parentExclude),
1651
- rating: normalizeArrayableValue(data?.rating)?.map((value) => Number(value)),
1652
- sku: data?.sku,
1653
- slug: data?.slug,
1740
+ rating: normalizeArrayableValue(data?.rating),
1741
+ sku: commaSeparated(data?.sku),
1742
+ slug: commaSeparated(data?.slug),
1654
1743
  stock_status: normalizeArrayableValue(data?.stockStatus),
1655
- tag: data?.tag,
1744
+ tag: commaSeparated(data?.tag),
1656
1745
  tag_operator: data?.tagOperator,
1657
1746
  type: data?.type,
1658
1747
  attributes: data?.attributes?.map((item) => ({
1659
1748
  operator: item.operator,
1660
- attribute: item.attribute,
1749
+ attribute: item.taxonomy,
1661
1750
  slug: normalizeArrayableValue(item.slug),
1662
1751
  term_id: normalizeArrayableValue(item.termId)
1663
1752
  })),
@@ -1670,49 +1759,68 @@ function serializeProductListInput(data) {
1670
1759
  related: data?.related,
1671
1760
  search: data?.search
1672
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;
1673
1768
  }
1674
- /**
1675
- * WooCommerce declares both of these fields as plain strings, so the contract cannot narrow them and
1676
- * neither can this without saying what it does with a value it does not recognize.
1677
- *
1678
- * A product type Kizlo has no name for reads as `simple`, which is what every gateway to a product
1679
- * page needs it to behave like. An unrecognized stock status is dropped from the filter list
1680
- * instead: a count nobody can label is not a filter anyone can offer.
1681
- */
1682
- function productType(type) {
1683
- return PRODUCT_TYPES.includes(type) ? type : "simple";
1769
+ function commaSeparated(value) {
1770
+ if (value === void 0) return void 0;
1771
+ return (Array.isArray(value) ? value : [value]).join(",");
1684
1772
  }
1685
1773
  function stockStatus(status) {
1686
1774
  return PRODUCT_STOCK_STATUSES.includes(status) ? status : null;
1687
1775
  }
1688
- function deserializeImages(images) {
1689
- return images.map((item) => ({
1690
- id: item.id,
1691
- src: item.src,
1692
- name: item.name,
1693
- alt: item.alt
1694
- }));
1695
- }
1696
- function deserializeTermRefs(terms) {
1697
- return terms.map((term) => ({
1776
+ function deserializeTermRef(term, taxonomy, urls) {
1777
+ return {
1698
1778
  id: term.id,
1699
1779
  name: term.name,
1700
- slug: term.slug
1780
+ slug: term.slug,
1781
+ url: urls[`${taxonomy}:${term.id}`] ?? null
1782
+ };
1783
+ }
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]];
1701
1789
  }));
1702
1790
  }
1703
- function toTimestamp(date) {
1704
- return new Date(date).getTime();
1791
+ function deserializeEmbeddedProducts(collections) {
1792
+ return (collections ?? []).flat().map(deserializeProductSummary);
1793
+ }
1794
+ function deserializeExtensions(value) {
1795
+ const { kizlo: rawKizlo,...extensions } = asRecord(value);
1796
+ return {
1797
+ extensions,
1798
+ kizlo: asRecord(rawKizlo)
1799
+ };
1800
+ }
1801
+ function asRecord(value) {
1802
+ return isRecord(value) ? value : {};
1803
+ }
1804
+ function isRecord(value) {
1805
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1806
+ }
1807
+ function nullableNumber(value) {
1808
+ return typeof value === "number" ? value : null;
1705
1809
  }
1706
1810
 
1707
1811
  //#endregion
1708
1812
  //#region src/product/index.ts
1813
+ const PRODUCT_EMBEDS = "upsells,cross_sells,related";
1709
1814
  const PRODUCT_PROCEDURES = {
1710
1815
  get: createProcedure({
1711
1816
  scope: "api",
1712
1817
  method: "GET",
1713
1818
  path: "/products/{identifier}",
1714
1819
  params: RetrieveProductInput.pick({ identifier: true }),
1715
- query: RetrieveProductInput.pick({ previewToken: true }).optional(),
1820
+ query: RetrieveProductInput.pick({
1821
+ previewToken: true,
1822
+ recommendations: true
1823
+ }).optional(),
1716
1824
  output: Product,
1717
1825
  errors: GET_PRODUCT_ERROR_MAP
1718
1826
  }, async ({ context, input, errors }) => {
@@ -1731,18 +1839,27 @@ const PRODUCT_PROCEDURES = {
1731
1839
  }
1732
1840
  return deserializeProduct(response$1.data);
1733
1841
  }
1734
- const response = await context.wordpress.woocommerce.products.list({ slug: String(input.params.identifier) });
1842
+ const identifier = input.params.identifier;
1843
+ const includeRecommendations = input.query?.recommendations ?? false;
1844
+ const embeds = includeRecommendations ? { _embed: PRODUCT_EMBEDS } : {};
1845
+ const response = typeof identifier === "number" ? await context.wordpress.woocommerce.store.products.getById({
1846
+ id: identifier,
1847
+ ...embeds
1848
+ }) : await context.wordpress.woocommerce.store.products.getBySlug({
1849
+ slug: identifier,
1850
+ ...embeds
1851
+ });
1735
1852
  if (response.error) switch (response.error.code) {
1853
+ case "woocommerce_rest_product_invalid_id":
1854
+ case "woocommerce_rest_product_invalid_slug": throw errors.PRODUCT_NOT_FOUND({ message: response.error.message });
1736
1855
  default:
1737
1856
  context.logger.error("Get product unhandled error", response.error, {
1738
- identifier: input.params.identifier,
1857
+ identifier,
1739
1858
  code: response.error.code
1740
1859
  });
1741
1860
  throw errors.INTERNAL_SERVER_ERROR();
1742
1861
  }
1743
- const data = response.data[0];
1744
- if (!data) throw errors.PRODUCT_NOT_FOUND();
1745
- return deserializeProduct(data);
1862
+ return deserializeStoreProduct(response.data, includeRecommendations ? deserializeProductRecommendations(response.data) : null);
1746
1863
  }),
1747
1864
  list: createProcedure({
1748
1865
  scope: "api",
@@ -1753,7 +1870,12 @@ const PRODUCT_PROCEDURES = {
1753
1870
  errors: LIST_PRODUCT_ERROR_MAP
1754
1871
  }, async ({ context, input, errors }) => {
1755
1872
  const searchParams = serializeProductListInput(input.query);
1756
- const response = await context.wordpress.woocommerce.store.products.list(searchParams);
1873
+ const includeRecommendations = input.query?.recommendations ?? false;
1874
+ const embeds = includeRecommendations ? { _embed: PRODUCT_EMBEDS } : {};
1875
+ const response = await context.wordpress.woocommerce.store.products.list({
1876
+ ...searchParams,
1877
+ ...embeds
1878
+ });
1757
1879
  if (response.error) switch (response.error.code) {
1758
1880
  default:
1759
1881
  context.logger.error("List products unhandled error", response.error, { code: response.error.code });
@@ -1765,7 +1887,7 @@ const PRODUCT_PROCEDURES = {
1765
1887
  searchParams: { ...searchParams }
1766
1888
  });
1767
1889
  return {
1768
- items: list.items.map(deserializeStoreProduct),
1890
+ items: list.items.map((item) => deserializeStoreProduct(item, includeRecommendations ? deserializeProductRecommendations(item) : null)),
1769
1891
  meta: deserializeListMetadata(list.meta)
1770
1892
  };
1771
1893
  }),
@@ -1780,12 +1902,12 @@ const PRODUCT_PROCEDURES = {
1780
1902
  const response = await context.wordpress.woocommerce.store.products.collectionData({
1781
1903
  ...searchParams,
1782
1904
  calculate_price_range: true,
1783
- calculate_rating_counts: input.query?.ratingFilters,
1784
- calculate_taxonomy_counts: input.query?.taxonomyFilters,
1785
- calculate_stock_status_counts: input.query?.stockStatusFilters,
1786
- calculate_attribute_counts: input.query?.attributeFilters?.map((item) => ({
1905
+ calculate_rating_counts: input.query?.ratingCounts,
1906
+ calculate_taxonomy_counts: input.query?.taxonomyCounts,
1907
+ calculate_stock_status_counts: input.query?.stockStatusCounts,
1908
+ calculate_attribute_counts: input.query?.attributeCounts?.map((item) => ({
1787
1909
  taxonomy: item.taxonomy,
1788
- query_type: item.queryType
1910
+ query_type: item.operator
1789
1911
  }))
1790
1912
  });
1791
1913
  if (response.error) switch (response.error.code) {
@@ -1805,7 +1927,7 @@ function woocommerce() {
1805
1927
  requires: {
1806
1928
  plugins: [{
1807
1929
  name: "kizlo-woocommerce",
1808
- version: "0.2.0"
1930
+ version: "0.3.0"
1809
1931
  }],
1810
1932
  endpoints: [
1811
1933
  "woocommerce.customers",
@@ -1826,4 +1948,4 @@ function woocommerce() {
1826
1948
  }
1827
1949
 
1828
1950
  //#endregion
1829
- export { woocommerce };
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 };