@kizlo/woocommerce 0.2.2 → 0.4.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, schemaType } 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,416 @@ 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 ProductCustomFieldsSchema = customFieldsSchema();
1393
+ const Product = ProductSummary.extend({
1394
+ weight: z.string(),
1395
+ dimensions: ProductDimensions,
1396
+ formattedWeight: z.string(),
1397
+ formattedDimensions: z.string(),
1398
+ stockQuantity: z.number().nullable(),
1399
+ saleStartsAt: z.number().nullable(),
1400
+ saleEndsAt: z.number().nullable(),
1401
+ seo: Seo.nullable(),
1402
+ custom: ProductCustomFieldsSchema,
1403
+ recommendations: ProductRecommendations.nullable()
1404
+ });
1405
+ const ProductList = z.object({
1406
+ items: z.array(Product),
1407
+ meta: ListMetadata
1408
+ });
1409
+ const RetrieveProductInput = z.object({
1410
+ identifier: IdentifierInput,
1411
+ previewToken: z.string().optional(),
1412
+ recommendations: BooleanLike.optional()
1413
+ });
1414
+ const PRODUCTS_ORDER_BYS = [
1415
+ "date",
1416
+ "modified",
1417
+ "id",
1418
+ "include",
1419
+ "title",
1420
+ "slug",
1421
+ "price",
1422
+ "popularity",
1423
+ "rating",
1424
+ "menu_order",
1425
+ "comment_count"
1426
+ ];
1427
+ const ProductOrderBy = z.enum(PRODUCTS_ORDER_BYS);
1428
+ const PRODUCT_DATE_COLUMNS = [
1429
+ "date",
1430
+ "date_gmt",
1431
+ "modified",
1432
+ "modified_gmt"
1433
+ ];
1434
+ const ProductDateColumn = z.enum(PRODUCT_DATE_COLUMNS);
1435
+ const PRODUCT_TAXONOMY_OPERATORS = [
1436
+ "in",
1437
+ "not_in",
1438
+ "and"
1439
+ ];
1440
+ const ProductTaxonomyOperator = z.enum(PRODUCT_TAXONOMY_OPERATORS);
1441
+ const PRODUCT_ATTRIBUTE_RELATIONS = ["in", "and"];
1442
+ const ProductAttributeRelation = z.enum(PRODUCT_ATTRIBUTE_RELATIONS);
1443
+ const PRODUCT_STOCK_STATUSES = [
1444
+ "instock",
1445
+ "outofstock",
1446
+ "onbackorder"
1447
+ ];
1448
+ const ProductStockStatus = z.enum(PRODUCT_STOCK_STATUSES);
1449
+ const PRODUCT_CATALOG_VISIBILITIES = [
1450
+ "any",
1451
+ "visible",
1452
+ "catalog",
1453
+ "search",
1454
+ "hidden"
1455
+ ];
1456
+ const ProductCatalogVisibility = z.enum(PRODUCT_CATALOG_VISIBILITIES);
1457
+ const PRODUCT_RATINGS = [
1458
+ 1,
1459
+ 2,
1460
+ 3,
1461
+ 4,
1462
+ 5
1463
+ ];
1464
+ const ProductRating = z.union([
1465
+ z.literal(1),
1466
+ z.literal(2),
1467
+ z.literal(3),
1468
+ z.literal(4),
1469
+ z.literal(5)
1470
+ ]);
1471
+ const ProductRatingInput = NumberLike.pipe(ProductRating);
1472
+ const ProductTermIdentifier = z.union([NumberLike, z.string()]);
1473
+ const ProductTaxonomyName = z.string().min(1).regex(/^[a-z0-9_-]+$/).refine((name) => !name.endsWith("_operator"));
1474
+ const ProductAttributeFilter = z.object({
1475
+ taxonomy: z.string(),
1476
+ slug: arrayable(z.string()).optional(),
1477
+ termId: arrayable(NumberLike).optional(),
1478
+ operator: ProductTaxonomyOperator.optional()
1479
+ });
1480
+ const ProductTaxonomyFilter = z.object({
1481
+ taxonomy: ProductTaxonomyName,
1482
+ termIds: arrayable(NumberLike).optional(),
1483
+ slugs: arrayable(z.string()).optional(),
1484
+ operator: ProductTaxonomyOperator.optional()
1485
+ }).refine((filter) => filter.termIds === void 0 !== (filter.slugs === void 0), { message: "Provide either termIds or slugs." });
1486
+ const ListProductInput = z.object({
1487
+ page: NumberLike.optional(),
1488
+ perPage: NumberLike.optional(),
1489
+ search: z.string().optional(),
1490
+ recommendations: BooleanLike.optional(),
1491
+ slug: arrayable(z.string()).optional(),
1492
+ after: z.string().optional(),
1493
+ before: z.string().optional(),
1494
+ dateColumn: ProductDateColumn.optional(),
1495
+ exclude: arrayable(NumberLike).optional(),
1496
+ include: arrayable(NumberLike).optional(),
1497
+ offset: NumberLike.optional(),
1498
+ order: z.enum(["asc", "desc"]).optional(),
1499
+ orderBy: ProductOrderBy.optional(),
1500
+ parent: arrayable(NumberLike).optional(),
1501
+ parentExclude: arrayable(NumberLike).optional(),
1502
+ type: z.string().optional(),
1503
+ sku: arrayable(z.string()).optional(),
1504
+ featured: BooleanLike.optional(),
1505
+ category: arrayable(ProductTermIdentifier).optional(),
1506
+ categoryOperator: ProductTaxonomyOperator.optional(),
1507
+ brand: arrayable(ProductTermIdentifier).optional(),
1508
+ brandOperator: ProductTaxonomyOperator.optional(),
1509
+ tag: arrayable(ProductTermIdentifier).optional(),
1510
+ tagOperator: ProductTaxonomyOperator.optional(),
1511
+ onSale: BooleanLike.optional(),
1512
+ minPrice: NumberLike.optional(),
1513
+ maxPrice: NumberLike.optional(),
1514
+ stockStatus: arrayable(ProductStockStatus).optional(),
1515
+ attributes: z.array(ProductAttributeFilter).optional(),
1516
+ attributeRelation: ProductAttributeRelation.optional(),
1517
+ catalogVisibility: ProductCatalogVisibility.optional(),
1518
+ rating: arrayable(ProductRatingInput).optional(),
1519
+ related: NumberLike.optional(),
1520
+ taxonomies: z.array(ProductTaxonomyFilter).optional()
1521
+ });
1522
+ const ProductFiltersPriceRange = z.object({
1523
+ minPrice: z.number(),
1524
+ maxPrice: z.number()
1525
+ });
1526
+ const ProductFiltersStockStatus = z.object({
1527
+ count: z.number(),
1528
+ status: ProductStockStatus
1529
+ });
1530
+ const ProductFiltersRatingCount = z.object({
1531
+ count: z.number(),
1532
+ rating: ProductRating
1533
+ });
1534
+ const ProductFiltersTerm = z.object({
1535
+ id: z.number(),
1536
+ parentId: z.number().nullable(),
1537
+ name: z.string(),
1538
+ slug: z.string(),
1539
+ taxonomy: z.string(),
1540
+ description: z.string(),
1541
+ count: z.number()
1542
+ });
1543
+ const ProductFiltersTaxonomyTerm = ProductFiltersTerm.extend({ image: MediaImage.nullable() });
1544
+ const ProductFiltersAttributeTerm = ProductFiltersTerm.extend({
1545
+ type: SwatchType,
1546
+ swatch: z.string().nullable()
1547
+ });
1548
+ const ProductFilters = z.object({
1549
+ priceRange: ProductFiltersPriceRange,
1550
+ ratingCounts: z.array(ProductFiltersRatingCount),
1551
+ stockStatuses: z.array(ProductFiltersStockStatus),
1552
+ attributeTerms: z.array(ProductFiltersAttributeTerm),
1553
+ taxonomyTerms: z.array(ProductFiltersTaxonomyTerm),
1554
+ currencyFormat: CurrencyFormat
1555
+ });
1556
+ const ProductAttributeCount = z.object({
1557
+ taxonomy: z.string(),
1558
+ operator: z.enum(["or", "and"]).optional()
1559
+ });
1560
+ const RetrieveProductFiltersInput = ListProductInput.omit({ recommendations: true }).extend({
1561
+ ratingCounts: BooleanLike.optional(),
1562
+ stockStatusCounts: BooleanLike.optional(),
1563
+ taxonomyCounts: z.array(z.string()).optional(),
1564
+ attributeCounts: z.array(ProductAttributeCount).optional()
1565
+ });
1566
+
1491
1567
  //#endregion
1492
1568
  //#region src/product/utils.ts
1569
+ function assertNoMissingStoreProductInputs() {}
1570
+ assertNoMissingStoreProductInputs();
1493
1571
  function deserializeProduct(data) {
1572
+ return deserializeStoreProduct(data.kizlo.store_product, null);
1573
+ }
1574
+ function deserializeStoreProduct(data, recommendations) {
1575
+ const summary = deserializeProductSummary(data);
1576
+ const { kizlo } = deserializeExtensions(data.extensions);
1494
1577
  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
1578
+ ...summary,
1579
+ weight: data.weight,
1580
+ dimensions: data.dimensions,
1581
+ formattedWeight: data.formatted_weight,
1582
+ formattedDimensions: data.formatted_dimensions,
1583
+ stockQuantity: nullableNumber(kizlo.stock),
1584
+ saleStartsAt: timestampFromIso(typeof kizlo.on_sale_from === "string" ? kizlo.on_sale_from : null),
1585
+ saleEndsAt: timestampFromIso(typeof kizlo.on_sale_to === "string" ? kizlo.on_sale_to : null),
1586
+ seo: isRecord(kizlo.seo) ? deserializeSeo(kizlo.seo) : null,
1587
+ custom: productCustomFields(kizlo.custom),
1588
+ recommendations
1536
1589
  };
1537
1590
  }
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
- };
1591
+ function deserializeProductSummary(data) {
1592
+ const { extensions, kizlo } = deserializeExtensions(data.extensions);
1593
+ const termUrls = deserializeTermUrls(kizlo.term_urls);
1545
1594
  return {
1546
1595
  id: data.id,
1547
- type: productType(data.type),
1548
1596
  name: data.name,
1549
1597
  slug: data.slug,
1550
- sku: data.sku,
1551
- description: data.description,
1598
+ parentId: data.parent === 0 ? null : data.parent,
1599
+ type: data.type,
1600
+ variationDescription: data.variation,
1601
+ url: typeof kizlo.url === "string" ? kizlo.url : null,
1602
+ sku: data.sku === "" ? null : data.sku,
1552
1603
  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,
1604
+ description: data.description,
1605
+ isPasswordProtected: data.is_password_protected,
1606
+ isOnSale: data.on_sale,
1558
1607
  prices: {
1559
- price: +data.prices.price,
1560
- salePrice: +data.prices.sale_price,
1561
- regularPrice: +data.prices.regular_price
1608
+ price: Number(data.prices.price),
1609
+ regularPrice: Number(data.prices.regular_price),
1610
+ salePrice: data.on_sale ? Number(data.prices.sale_price) : null,
1611
+ priceRange: data.prices.price_range ? {
1612
+ minAmount: Number(data.prices.price_range.min_amount),
1613
+ maxAmount: Number(data.prices.price_range.max_amount)
1614
+ } : null
1562
1615
  },
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
1616
  currencyFormat: deserializeCurrencyFormat(data.prices),
1574
- brands: data.brands,
1575
- groupedProducts: data.grouped_products,
1617
+ priceHtml: data.price_html,
1618
+ averageRating: Number(data.average_rating),
1619
+ reviewCount: data.review_count,
1620
+ images: data.images.map((image) => ({
1621
+ type: "image",
1622
+ id: image.id,
1623
+ src: image.src,
1624
+ srcset: image.srcset,
1625
+ name: image.name,
1626
+ alt: image.alt
1627
+ })),
1628
+ categories: data.categories.map((term) => deserializeTermRef(term, "product_cat", termUrls)),
1629
+ tags: data.tags.map((term) => deserializeTermRef(term, "product_tag", termUrls)),
1630
+ brands: data.brands.map((term) => deserializeTermRef(term, "product_brand", termUrls)),
1631
+ attributes: data.attributes.map((attribute) => ({
1632
+ id: attribute.id,
1633
+ name: attribute.name,
1634
+ taxonomy: attribute.taxonomy ?? null,
1635
+ hasVariations: attribute.has_variations,
1636
+ terms: attribute.terms.map((term) => ({
1637
+ id: term.id,
1638
+ name: term.name,
1639
+ slug: term.slug,
1640
+ isDefault: term.default
1641
+ }))
1642
+ })),
1643
+ variations: data.variations.map((variation) => ({
1644
+ id: variation.id,
1645
+ attributes: variation.attributes.map((attribute) => ({
1646
+ name: attribute.name,
1647
+ value: attribute.value ?? null
1648
+ }))
1649
+ })),
1650
+ groupedProductIds: data.grouped_products,
1576
1651
  hasOptions: data.has_options,
1577
- isOnBackorder: data.is_on_backorder,
1578
1652
  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: {}
1653
+ isInStock: data.is_in_stock,
1654
+ isOnBackorder: data.is_on_backorder,
1655
+ stockAvailability: data.stock_availability,
1656
+ lowStockRemaining: data.low_stock_remaining,
1657
+ isSoldIndividually: data.sold_individually,
1658
+ addToCart: {
1659
+ text: data.add_to_cart.text,
1660
+ description: data.add_to_cart.description,
1661
+ singleText: data.add_to_cart.single_text,
1662
+ minimum: data.add_to_cart.minimum,
1663
+ maximum: data.add_to_cart.maximum,
1664
+ multipleOf: data.add_to_cart.multiple_of
1665
+ },
1666
+ extensions
1667
+ };
1668
+ }
1669
+ function deserializeProductRecommendations(data) {
1670
+ return {
1671
+ upsells: deserializeEmbeddedProducts(data._embedded?.upsells),
1672
+ crossSells: deserializeEmbeddedProducts(data._embedded?.cross_sells),
1673
+ related: deserializeEmbeddedProducts(data._embedded?.related)
1587
1674
  };
1588
1675
  }
1676
+ function productCustomFields(value) {
1677
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
1678
+ }
1589
1679
  function deserializeProductFilters(data) {
1590
1680
  if (!data.price_range) return null;
1591
1681
  const maxPrice = +data.price_range.max_price;
1592
1682
  const minPrice = +data.price_range.min_price;
1593
1683
  return {
1684
+ ratingCounts: (data.rating_counts ?? []).flatMap((entry) => PRODUCT_RATINGS.includes(entry.rating) ? [{
1685
+ count: entry.count,
1686
+ rating: entry.rating
1687
+ }] : []),
1594
1688
  stockStatuses: (data.stock_status_counts ?? []).flatMap((entry) => {
1595
1689
  const status = stockStatus(entry.status);
1596
1690
  return status ? [{
@@ -1606,12 +1700,7 @@ function deserializeProductFilters(data) {
1606
1700
  parentId: item.parent,
1607
1701
  slug: item.slug,
1608
1702
  taxonomy: item.taxonomy,
1609
- image: item.thumbnail ? {
1610
- id: 0,
1611
- alt: item.name,
1612
- name: item.name,
1613
- src: item.thumbnail
1614
- } : null
1703
+ image: item.image
1615
1704
  })),
1616
1705
  attributeTerms: data.kizlo.attribute_counts.map((item) => ({
1617
1706
  id: item.id,
@@ -1632,32 +1721,33 @@ function deserializeProductFilters(data) {
1632
1721
  };
1633
1722
  }
1634
1723
  function serializeProductListInput(data) {
1635
- return {
1724
+ const searchParams = {
1636
1725
  after: data?.after,
1637
1726
  attribute_relation: data?.attributeRelation,
1638
1727
  before: data?.before,
1639
- brand: data?.brand,
1728
+ brand: commaSeparated(data?.brand),
1640
1729
  brand_operator: data?.brandOperator,
1641
1730
  catalog_visibility: data?.catalogVisibility,
1642
- category: data?.category,
1731
+ category: commaSeparated(data?.category),
1643
1732
  category_operator: data?.categoryOperator,
1733
+ date_column: data?.dateColumn,
1644
1734
  featured: data?.featured,
1645
- max_price: data?.maxPrice ? String(data?.maxPrice) : void 0,
1646
- min_price: data?.minPrice ? String(data?.minPrice) : void 0,
1735
+ max_price: data?.maxPrice === void 0 ? void 0 : String(data.maxPrice),
1736
+ min_price: data?.minPrice === void 0 ? void 0 : String(data.minPrice),
1647
1737
  on_sale: data?.onSale,
1648
- orderby: data?.orderby,
1738
+ orderby: data?.orderBy,
1649
1739
  parent: normalizeArrayableValue(data?.parent),
1650
1740
  parent_exclude: normalizeArrayableValue(data?.parentExclude),
1651
- rating: normalizeArrayableValue(data?.rating)?.map((value) => Number(value)),
1652
- sku: data?.sku,
1653
- slug: data?.slug,
1741
+ rating: normalizeArrayableValue(data?.rating),
1742
+ sku: commaSeparated(data?.sku),
1743
+ slug: commaSeparated(data?.slug),
1654
1744
  stock_status: normalizeArrayableValue(data?.stockStatus),
1655
- tag: data?.tag,
1745
+ tag: commaSeparated(data?.tag),
1656
1746
  tag_operator: data?.tagOperator,
1657
1747
  type: data?.type,
1658
1748
  attributes: data?.attributes?.map((item) => ({
1659
1749
  operator: item.operator,
1660
- attribute: item.attribute,
1750
+ attribute: item.taxonomy,
1661
1751
  slug: normalizeArrayableValue(item.slug),
1662
1752
  term_id: normalizeArrayableValue(item.termId)
1663
1753
  })),
@@ -1670,50 +1760,69 @@ function serializeProductListInput(data) {
1670
1760
  related: data?.related,
1671
1761
  search: data?.search
1672
1762
  };
1763
+ for (const filter of data?.taxonomies ?? []) {
1764
+ const key = `_unstable_tax_${filter.taxonomy}`;
1765
+ searchParams[key] = commaSeparated(filter.termIds ?? filter.slugs);
1766
+ if (filter.operator !== void 0) searchParams[`${key}_operator`] = filter.operator;
1767
+ }
1768
+ return searchParams;
1673
1769
  }
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";
1770
+ function commaSeparated(value) {
1771
+ if (value === void 0) return void 0;
1772
+ return (Array.isArray(value) ? value : [value]).join(",");
1684
1773
  }
1685
1774
  function stockStatus(status) {
1686
1775
  return PRODUCT_STOCK_STATUSES.includes(status) ? status : null;
1687
1776
  }
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) => ({
1777
+ function deserializeTermRef(term, taxonomy, urls) {
1778
+ return {
1698
1779
  id: term.id,
1699
1780
  name: term.name,
1700
- slug: term.slug
1781
+ slug: term.slug,
1782
+ url: urls[`${taxonomy}:${term.id}`] ?? null
1783
+ };
1784
+ }
1785
+ function deserializeTermUrls(value) {
1786
+ if (!Array.isArray(value)) return {};
1787
+ return Object.fromEntries(value.flatMap((item) => {
1788
+ if (!isRecord(item) || typeof item.id !== "number" || typeof item.taxonomy !== "string" || typeof item.url !== "string") return [];
1789
+ return [[`${item.taxonomy}:${item.id}`, item.url]];
1701
1790
  }));
1702
1791
  }
1703
- function toTimestamp(date) {
1704
- return new Date(date).getTime();
1792
+ function deserializeEmbeddedProducts(collections) {
1793
+ return (collections ?? []).flat().map(deserializeProductSummary);
1794
+ }
1795
+ function deserializeExtensions(value) {
1796
+ const { kizlo: rawKizlo,...extensions } = asRecord(value);
1797
+ return {
1798
+ extensions,
1799
+ kizlo: asRecord(rawKizlo)
1800
+ };
1801
+ }
1802
+ function asRecord(value) {
1803
+ return isRecord(value) ? value : {};
1804
+ }
1805
+ function isRecord(value) {
1806
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1807
+ }
1808
+ function nullableNumber(value) {
1809
+ return typeof value === "number" ? value : null;
1705
1810
  }
1706
1811
 
1707
1812
  //#endregion
1708
1813
  //#region src/product/index.ts
1814
+ const PRODUCT_EMBEDS = "upsells,cross_sells,related";
1709
1815
  const PRODUCT_PROCEDURES = {
1710
1816
  get: createProcedure({
1711
1817
  scope: "api",
1712
1818
  method: "GET",
1713
1819
  path: "/products/{identifier}",
1714
1820
  params: RetrieveProductInput.pick({ identifier: true }),
1715
- query: RetrieveProductInput.pick({ previewToken: true }).optional(),
1716
- output: Product,
1821
+ query: RetrieveProductInput.pick({
1822
+ previewToken: true,
1823
+ recommendations: true
1824
+ }).optional(),
1825
+ output: schemaType(Product),
1717
1826
  errors: GET_PRODUCT_ERROR_MAP
1718
1827
  }, async ({ context, input, errors }) => {
1719
1828
  if (input.query?.previewToken) {
@@ -1731,29 +1840,43 @@ const PRODUCT_PROCEDURES = {
1731
1840
  }
1732
1841
  return deserializeProduct(response$1.data);
1733
1842
  }
1734
- const response = await context.wordpress.woocommerce.products.list({ slug: String(input.params.identifier) });
1843
+ const identifier = input.params.identifier;
1844
+ const includeRecommendations = input.query?.recommendations ?? false;
1845
+ const embeds = includeRecommendations ? { _embed: PRODUCT_EMBEDS } : {};
1846
+ const response = typeof identifier === "number" ? await context.wordpress.woocommerce.store.products.getById({
1847
+ id: identifier,
1848
+ ...embeds
1849
+ }) : await context.wordpress.woocommerce.store.products.getBySlug({
1850
+ slug: identifier,
1851
+ ...embeds
1852
+ });
1735
1853
  if (response.error) switch (response.error.code) {
1854
+ case "woocommerce_rest_product_invalid_id":
1855
+ case "woocommerce_rest_product_invalid_slug": throw errors.PRODUCT_NOT_FOUND({ message: response.error.message });
1736
1856
  default:
1737
1857
  context.logger.error("Get product unhandled error", response.error, {
1738
- identifier: input.params.identifier,
1858
+ identifier,
1739
1859
  code: response.error.code
1740
1860
  });
1741
1861
  throw errors.INTERNAL_SERVER_ERROR();
1742
1862
  }
1743
- const data = response.data[0];
1744
- if (!data) throw errors.PRODUCT_NOT_FOUND();
1745
- return deserializeProduct(data);
1863
+ return deserializeStoreProduct(response.data, includeRecommendations ? deserializeProductRecommendations(response.data) : null);
1746
1864
  }),
1747
1865
  list: createProcedure({
1748
1866
  scope: "api",
1749
1867
  method: "GET",
1750
1868
  path: "/products",
1751
1869
  query: ListProductInput.optional(),
1752
- output: ProductList,
1870
+ output: schemaType(ProductList),
1753
1871
  errors: LIST_PRODUCT_ERROR_MAP
1754
1872
  }, async ({ context, input, errors }) => {
1755
1873
  const searchParams = serializeProductListInput(input.query);
1756
- const response = await context.wordpress.woocommerce.store.products.list(searchParams);
1874
+ const includeRecommendations = input.query?.recommendations ?? false;
1875
+ const embeds = includeRecommendations ? { _embed: PRODUCT_EMBEDS } : {};
1876
+ const response = await context.wordpress.woocommerce.store.products.list({
1877
+ ...searchParams,
1878
+ ...embeds
1879
+ });
1757
1880
  if (response.error) switch (response.error.code) {
1758
1881
  default:
1759
1882
  context.logger.error("List products unhandled error", response.error, { code: response.error.code });
@@ -1765,7 +1888,7 @@ const PRODUCT_PROCEDURES = {
1765
1888
  searchParams: { ...searchParams }
1766
1889
  });
1767
1890
  return {
1768
- items: list.items.map(deserializeStoreProduct),
1891
+ items: list.items.map((item) => deserializeStoreProduct(item, includeRecommendations ? deserializeProductRecommendations(item) : null)),
1769
1892
  meta: deserializeListMetadata(list.meta)
1770
1893
  };
1771
1894
  }),
@@ -1780,12 +1903,12 @@ const PRODUCT_PROCEDURES = {
1780
1903
  const response = await context.wordpress.woocommerce.store.products.collectionData({
1781
1904
  ...searchParams,
1782
1905
  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) => ({
1906
+ calculate_rating_counts: input.query?.ratingCounts,
1907
+ calculate_taxonomy_counts: input.query?.taxonomyCounts,
1908
+ calculate_stock_status_counts: input.query?.stockStatusCounts,
1909
+ calculate_attribute_counts: input.query?.attributeCounts?.map((item) => ({
1787
1910
  taxonomy: item.taxonomy,
1788
- query_type: item.queryType
1911
+ query_type: item.operator
1789
1912
  }))
1790
1913
  });
1791
1914
  if (response.error) switch (response.error.code) {
@@ -1805,7 +1928,7 @@ function woocommerce() {
1805
1928
  requires: {
1806
1929
  plugins: [{
1807
1930
  name: "kizlo-woocommerce",
1808
- version: "0.2.0"
1931
+ version: "0.3.0"
1809
1932
  }],
1810
1933
  endpoints: [
1811
1934
  "woocommerce.customers",
@@ -1826,4 +1949,4 @@ function woocommerce() {
1826
1949
  }
1827
1950
 
1828
1951
  //#endregion
1829
- export { woocommerce };
1952
+ export { ListProductInput, PRODUCT_ATTRIBUTE_RELATIONS, PRODUCT_CATALOG_VISIBILITIES, PRODUCT_DATE_COLUMNS, PRODUCT_RATINGS, PRODUCT_STOCK_STATUSES, PRODUCT_TYPES, Product, ProductAddToCart, ProductAttributeCount, ProductAttributeFilter, ProductAttributeRelation, ProductAttributeSummary, ProductAttributeTermSummary, ProductBrandSummary, ProductCatalogVisibility, ProductCategorySummary, ProductCustomFieldsSchema, ProductDateColumn, ProductDimensions, ProductExtensions, ProductFilters, ProductFiltersAttributeTerm, ProductFiltersPriceRange, ProductFiltersRatingCount, ProductFiltersStockStatus, ProductFiltersTaxonomyTerm, ProductFiltersTerm, ProductList, ProductOrderBy, ProductPriceRange, ProductPrices, ProductRating, ProductRecommendations, ProductStockAvailability, ProductStockStatus, ProductSummary, ProductTagSummary, ProductTaxonomyFilter, ProductTaxonomyOperator, ProductTermSummary, ProductType, ProductVariationAttributeSummary, ProductVariationSummary, RetrieveProductFiltersInput, RetrieveProductInput, SWATCH_TYPES, SwatchType, woocommerce };