@01.software/sdk 0.39.0 → 0.41.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +492 -0
  2. package/MIGRATION.md +183 -0
  3. package/README.md +64 -21
  4. package/dist/client.cjs +47 -69
  5. package/dist/client.cjs.map +1 -1
  6. package/dist/client.d.cts +6 -6
  7. package/dist/client.d.ts +6 -6
  8. package/dist/client.js +47 -69
  9. package/dist/client.js.map +1 -1
  10. package/dist/{collection-client-DVfB0Em1.d.cts → collection-client-Bymfni8u.d.cts} +3 -3
  11. package/dist/{collection-client-CaMgs5KE.d.ts → collection-client-CN4lj6gi.d.ts} +3 -3
  12. package/dist/const-4BUtUdGU.d.cts +32 -0
  13. package/dist/const-ymprfiPf.d.ts +32 -0
  14. package/dist/{index-BOLQxveo.d.cts → index-Dquv4xTL.d.cts} +3 -3
  15. package/dist/{index-CSwR2HSg.d.ts → index-w36lpSkU.d.ts} +3 -3
  16. package/dist/index.cjs +119 -136
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +8 -8
  19. package/dist/index.d.ts +8 -8
  20. package/dist/index.js +119 -136
  21. package/dist/index.js.map +1 -1
  22. package/dist/{payload-types-m3jjhxk9.d.cts → payload-types-DC0xzR9i.d.cts} +470 -265
  23. package/dist/{payload-types-m3jjhxk9.d.ts → payload-types-DC0xzR9i.d.ts} +470 -265
  24. package/dist/query.d.cts +5 -5
  25. package/dist/query.d.ts +5 -5
  26. package/dist/realtime.d.cts +2 -2
  27. package/dist/realtime.d.ts +2 -2
  28. package/dist/server.cjs +40 -69
  29. package/dist/server.cjs.map +1 -1
  30. package/dist/server.d.cts +7 -7
  31. package/dist/server.d.ts +7 -7
  32. package/dist/server.js +40 -69
  33. package/dist/server.js.map +1 -1
  34. package/dist/{types-Cmrd1ezc.d.ts → types-Bh2p-EMc.d.ts} +5 -1
  35. package/dist/{types-CVf8sCZ-.d.ts → types-Bl-m9ttd.d.cts} +129 -77
  36. package/dist/{types-D0ubzQw0.d.cts → types-BvpjooU-.d.cts} +5 -1
  37. package/dist/{types-BQo7UdI9.d.cts → types-CHrzs2GB.d.ts} +129 -77
  38. package/dist/ui/form.d.cts +1 -1
  39. package/dist/ui/form.d.ts +1 -1
  40. package/dist/ui/video.d.cts +1 -1
  41. package/dist/ui/video.d.ts +1 -1
  42. package/dist/webhook.d.cts +4 -4
  43. package/dist/webhook.d.ts +4 -4
  44. package/package.json +6 -4
  45. package/dist/const-6XHz_jej.d.ts +0 -32
  46. package/dist/const-B5KT72c7.d.cts +0 -32
package/README.md CHANGED
@@ -126,7 +126,7 @@ const client = createClient({
126
126
  // Query data (returns Payload native response)
127
127
  const { docs } = await client.collections.from('products').find({
128
128
  limit: 10,
129
- where: { status: { equals: 'published' } },
129
+ select: { title: true, slug: true },
130
130
  })
131
131
  ```
132
132
 
@@ -264,7 +264,8 @@ if (!result.found) {
264
264
  }
265
265
 
266
266
  const { product } = result
267
- // product: { product, variants, options, brand, categories, tags, images, videos, listing }
267
+ // product: { product, variants, options, brand, categories, tags, images, videos,
268
+ // featuredImage, priceRange, compareAtPriceRange, availableForSale, selectedOrFirstAvailableVariant }
268
269
  ```
269
270
 
270
271
  `detail()` returns `ProductDetailResult`, a discriminated union:
@@ -276,6 +277,15 @@ mismatches, still throw
276
277
  typed `SDKError` subclasses and preserve request IDs through the existing
277
278
  `lastRequestId` / `onRequestId` path.
278
279
 
280
+ Public product visibility has two axes. Lifecycle `status` must still be
281
+ `published` and `publishedAt` must be current or empty. Then
282
+ `storefrontVisibility` decides storefront exposure: `listed` products appear in
283
+ listing helpers and direct detail, `unlisted` products are omitted from listing
284
+ helpers but can be fetched by direct detail slug/id, and `hidden` products return
285
+ `{ found: false, reason: 'not_published' }` from detail and are omitted from
286
+ listings. Legacy products without `storefrontVisibility` are treated as
287
+ `listed`.
288
+
279
289
  The successful product payload exposes inventory rollups without sentinel
280
290
  values: `product.totalInventory` is the tracked stock sum across non-unlimited
281
291
  variants, `null` when no variants are tracked, and
@@ -353,7 +363,10 @@ does not expose), use `createServerClient()` and spread
353
363
  `PRODUCT_PLP_FIND_OPTIONS` to raise the default Payload join limit of 10:
354
364
 
355
365
  ```typescript
356
- import { PRODUCT_PLP_FIND_OPTIONS } from '@01.software/sdk'
366
+ import {
367
+ PRODUCT_PLP_FIND_OPTIONS,
368
+ projectProductToListingShape,
369
+ } from '@01.software/sdk'
357
370
  import { createServerClient } from '@01.software/sdk/server'
358
371
 
359
372
  const server = createServerClient({
@@ -362,11 +375,32 @@ const server = createServerClient({
362
375
  secretKey: process.env.SOFTWARE_SECRET_KEY!,
363
376
  })
364
377
 
378
+ const now = new Date().toISOString()
365
379
  const { docs } = await server.collections.from('products').find({
366
380
  ...PRODUCT_PLP_FIND_OPTIONS,
367
- where: { status: { equals: 'published' } },
381
+ where: {
382
+ and: [
383
+ { status: { equals: 'published' } },
384
+ {
385
+ or: [
386
+ { publishedAt: { less_than_equal: now } },
387
+ { publishedAt: { exists: false } },
388
+ { publishedAt: { equals: null } },
389
+ ],
390
+ },
391
+ {
392
+ or: [
393
+ { storefrontVisibility: { equals: 'listed' } },
394
+ { storefrontVisibility: { exists: false } },
395
+ { storefrontVisibility: { equals: null } },
396
+ ],
397
+ },
398
+ ],
399
+ },
368
400
  limit: 24,
369
401
  })
402
+
403
+ const listingProducts = docs.map(projectProductToListingShape)
370
404
  ```
371
405
 
372
406
  `PRODUCT_PLP_FIND_OPTIONS` sets `joins.variants` and `joins.options` to safe
@@ -465,11 +499,11 @@ const selectionQuery = codec.stringify(normalizedSelection)
465
499
  #### Empty vs partial selection
466
500
 
467
501
  When selection input is omitted, `resolveProductSelection()` applies
468
- `listing.selectionHintVariant` so PDP defaults match listing cards. That is
502
+ `selectedOrFirstAvailableVariant` so PDP defaults match listing cards. That is
469
503
  separate from `fillDefaults` and is not gated by a flag.
470
504
 
471
505
  ```typescript
472
- // PDP default (uses listing.selectionHintVariant when selection is omitted)
506
+ // PDP default (uses selectedOrFirstAvailableVariant when selection is omitted)
473
507
  resolveProductSelection(product)
474
508
 
475
509
  // Catalog: keep price range / no concrete variant
@@ -489,7 +523,7 @@ const resolution = resolveProductSelection(
489
523
  },
490
524
  )
491
525
  // resolution.selectedVariant is concrete; unselected options are filled
492
- // using the same available-by-order rules as listing selectionHintVariant.
526
+ // using the same available-by-order rule that derives selectedOrFirstAvailableVariant.
493
527
  ```
494
528
 
495
529
  For option-click handlers, use `selectNext()` to apply a slug transition,
@@ -548,16 +582,19 @@ inspect grouped variant fields without a follow-up fetch. The by-ids response
548
582
  also returns `missing: string[]` for requested product IDs that were not found,
549
583
  not published, or not accessible; `docs` preserve the input `productIds` order
550
584
  for returned products. The helper populates optional `representativeVariant`, a
551
- PDP-seeded `href`, representative media (`product.thumbnail` -> first
552
- `product.images` -> representative variant media -> `null`), an aggregated price
553
- range across all option-value groups, and a `swatches[]` array derived from
554
- groups when there is more than one. Single-group products emit `swatches: []`;
555
- storefronts that disagree can read `item.groups` directly.
585
+ PDP-seeded `href`, representative media from Shopify-shaped
586
+ `product.featuredImage` with product gallery fallback, Product-level
587
+ `priceRange` / `compareAtPriceRange`, Product-level `availableForSale`, and a
588
+ `swatches[]` array derived from groups when there is more than one. Single-group
589
+ products emit `swatches: []`; storefronts that disagree can read `item.groups`
590
+ directly.
556
591
 
557
592
  `buildProductListingCard()` derives card swatches from listing-group
558
593
  `optionValueSwatch`. Image swatches use `swatch.mediaItemId`; color swatches use
559
594
  `swatch.color`. Option-value thumbnail/gallery fields are no longer part of the
560
- public listing-group or product-detail contract.
595
+ public listing-group or product-detail contract. New PLP filters and sorts
596
+ should use Product-shaped names such as `priceRange.minVariantPrice.amount`,
597
+ `priceRange.maxVariantPrice.amount`, and `availableForSale`.
561
598
 
562
599
  ```ts
563
600
  import {
@@ -669,7 +706,7 @@ Dotted-path filters (`where: { 'product.slug': { equals } }`) are Payload-native
669
706
 
670
707
  Checklist when `find()` returns `docs: []` unexpectedly, in order of likelihood:
671
708
 
672
- 1. **Access control filtered the document.** Many collections enforce status/published filters on public read (e.g. `composedReadStatusPublished` on `products` restricts unauthenticated reads to `status: 'published'`). A draft or unpublished document silently disappears from results even when its slug matches. Correlate with backend logs via `client.lastRequestId` (or catch `SDKError.requestId`).
709
+ 1. **Access control filtered the document.** Many collections enforce public read filters. Products require `status: 'published'`, a current or unset `publishedAt`, and listable storefront visibility (`listed`, legacy missing, or `null`) for publishable-key raw listing reads. Draft, future, `unlisted`, `hidden`, or malformed products silently disappear from raw listing results even when their slug or ID matches. Use the shaped product detail helper for direct-link `unlisted` products. Correlate with backend logs via `client.lastRequestId` (or catch `SDKError.requestId`).
673
710
  2. **Build-time publishable key / API URL differs from runtime.** SSG `generateStaticParams` / `generateMetadata` / the page render must all see the same tenant context. A wrong or missing key at build time produces a baked-in empty response.
674
711
  3. **Next.js SSG fetch cache served a stale empty response.** Use `cache: 'no-store'` or `export const revalidate = 0` on server components that should reflect live data.
675
712
  4. **`where: { slug: 'x' }` string shorthand.** Always use `{ slug: { equals: 'x' } }` — bare strings silently match nothing.
@@ -749,7 +786,6 @@ const { docs, totalDocs, hasNextPage } = await client.collections
749
786
  limit: 20,
750
787
  page: 1,
751
788
  sort: '-createdAt',
752
- where: { status: { equals: 'published' } },
753
789
  depth: 0,
754
790
  select: { title: true, slug: true },
755
791
  })
@@ -981,7 +1017,7 @@ client.customer.auth.logout()
981
1017
  const orders = await client.commerce.orders.listMine({
982
1018
  page: 1,
983
1019
  limit: 10,
984
- status: 'paid',
1020
+ displayFinancialStatus: 'paid',
985
1021
  })
986
1022
 
987
1023
  // Password
@@ -992,7 +1028,7 @@ await client.customer.auth.changePassword(currentPassword, newPassword)
992
1028
 
993
1029
  ### Commerce Orders (ServerClient-only writes)
994
1030
 
995
- Available on ServerClient via `server.commerce.orders.*`. `checkout` and `listMine` are also on Client and return sanitized customer-facing order DTOs. Use `server.collections.from('orders')` for raw operational order documents.
1031
+ Available on ServerClient via `server.commerce.orders.*`. `checkout` and `listMine` are also on Client and return sanitized customer-facing order DTOs. Customer order DTOs expose the independent status axes (`displayFinancialStatus`, `displayFulfillmentStatus`, `returnStatus`, `primaryDisplayStatus`, `returnDisplayStatus`); `status` and `displayStatus` are read-only compatibility aliases for older clients. Use `server.collections.from('orders')` for raw operational order documents.
996
1032
 
997
1033
  ```typescript
998
1034
  // Orders
@@ -1307,7 +1343,7 @@ join-order surface and does not emit a semantic order-change webhook.
1307
1343
 
1308
1344
  ## Supported Collections
1309
1345
 
1310
- Source of truth: `packages/sdk/src/core/collection/const.ts` (`COLLECTIONS`: 53).
1346
+ Source of truth: `packages/sdk/src/core/collection/const.ts` (`COLLECTIONS`: 51).
1311
1347
 
1312
1348
  | Category | Browser-public generic collections |
1313
1349
  | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
@@ -1318,7 +1354,7 @@ Source of truth: `packages/sdk/src/core/collection/const.ts` (`COLLECTIONS`: 53)
1318
1354
  | Content | `documents`, `document-categories`, `document-types`, `articles`, `article-authors`, `article-categories`, `article-tags`, `links`, `link-categories`, `link-tags` |
1319
1355
  | Playlists / Tracks | `playlists`, `playlist-categories`, `playlist-tags`, `tracks`, `track-categories`, `track-tags` |
1320
1356
  | Galleries | `galleries`, `gallery-categories`, `gallery-tags`, `gallery-items` |
1321
- | Canvas | `canvases`, `canvas-node-types`, `canvas-edge-types`, `canvas-categories`, `canvas-tags`, `canvas-nodes`, `canvas-edges` |
1357
+ | Canvas | `canvases`, `canvas-node-types`, `canvas-edge-types`, `canvas-categories`, `canvas-tags` |
1322
1358
  | Videos | `video-categories`, `video-tags` |
1323
1359
  | Forms | `forms` |
1324
1360
  | Community | `reaction-types`, `post-categories`, `post-tags`, `customer-profile-lists` |
@@ -1327,7 +1363,8 @@ Source of truth: `packages/sdk/src/core/collection/const.ts` (`COLLECTIONS`: 53)
1327
1363
  Server-only collections include raw media/logo records, customer and order
1328
1364
  operational records, raw cart/cart-item records, form submissions, raw community documents
1329
1365
  (`posts`, `comments`, `reactions`, `bookmarks`), live stream provider records,
1330
- segmentation records, and moderation records. They remain available from
1366
+ Canvas graph shell rows (`canvas-nodes`, `canvas-edges`), segmentation records,
1367
+ and moderation records. They remain available from
1331
1368
  `createServerClient().collections` with secret/PAT credentials. Shaped
1332
1369
  browser/customer helpers such as `commerce.cart.*`,
1333
1370
  `commerce.orders.listMine()`, and `commerce.orders.checkout()` remain available
@@ -1455,6 +1492,12 @@ API keys created without explicit scopes use the default `['read', 'write']`. Co
1455
1492
 
1456
1493
  ## Changelog
1457
1494
 
1495
+ > The complete, up-to-date release history (including 0.35.0+) lives in
1496
+ > [`CHANGELOG.md`](./CHANGELOG.md), and breaking-change migration steps live in
1497
+ > [`MIGRATION.md`](./MIGRATION.md). Both ship with the published package. The
1498
+ > selected entries below are kept inline for historically significant breaking
1499
+ > changes.
1500
+
1458
1501
  ### v0.23.0 (Product option-value visuals)
1459
1502
 
1460
1503
  - Added reusable option-value visuals (nested `swatch`, `thumbnail`, `images`) to Payload types and ecommerce utility shapes.
@@ -1477,7 +1520,7 @@ Migration steps:
1477
1520
 
1478
1521
  1. Replace color-only values with `swatch: { type: 'color', color: '#111111' }`.
1479
1522
  2. Replace thumbnail/gallery values with `swatch: { type: 'media', mediaItemId: '<product-pool-image-id>' }`.
1480
- 3. Ensure media swatches reference an image already attached to the parent product (`products.images` / `products.thumbnail`).
1523
+ 3. Ensure media swatches reference an image already attached to the parent product (`products.images`).
1481
1524
  4. Remove reads of pre-ADR-0025 response fields; consume `optionValueSwatch` / `optionValue.swatch` instead.
1482
1525
 
1483
1526
  ## Migration Guide
package/dist/client.cjs CHANGED
@@ -1479,6 +1479,19 @@ var CustomerAuth = class {
1479
1479
  const params = new URLSearchParams();
1480
1480
  if (options?.page) params.set("page", String(options.page));
1481
1481
  if (options?.limit) params.set("limit", String(options.limit));
1482
+ if (options?.displayFinancialStatus) {
1483
+ params.set("displayFinancialStatus", options.displayFinancialStatus);
1484
+ }
1485
+ if (options?.displayFulfillmentStatus) {
1486
+ params.set("displayFulfillmentStatus", options.displayFulfillmentStatus);
1487
+ }
1488
+ if (options?.returnStatus) params.set("returnStatus", options.returnStatus);
1489
+ if (options?.primaryDisplayStatus) {
1490
+ params.set("primaryDisplayStatus", options.primaryDisplayStatus);
1491
+ }
1492
+ if (options?.returnDisplayStatus) {
1493
+ params.set("returnDisplayStatus", options.returnDisplayStatus);
1494
+ }
1482
1495
  if (options?.status) params.set("status", options.status);
1483
1496
  const qs = params.toString();
1484
1497
  return this.requestJson(`/api/customers/me/orders${qs ? `?${qs}` : ""}`, {
@@ -1593,9 +1606,6 @@ function getMediaId(value) {
1593
1606
  }
1594
1607
  return null;
1595
1608
  }
1596
- function toPointerId(value) {
1597
- return getMediaId(value);
1598
- }
1599
1609
  function mediaArray(value) {
1600
1610
  if (!Array.isArray(value)) return [];
1601
1611
  return value.filter((entry) => entry != null);
@@ -1712,22 +1722,16 @@ function resolveProductSelectionMedia(input) {
1712
1722
  source: "none"
1713
1723
  };
1714
1724
  }
1715
- function resolveListingPrimaryImagePointer(input) {
1725
+ function resolveFeaturedImagePointer(input) {
1716
1726
  const pool = mediaArray(input.productMediaPool);
1717
1727
  const resolvedPointer = getMediaId(input.resolvedPrimary);
1718
1728
  if (resolvedPointer && input.resolvedSource !== "product_pool" && input.resolvedSource !== "none") {
1719
1729
  return resolvedPointer;
1720
1730
  }
1721
1731
  const poolById = buildPoolById(pool);
1722
- const listingPointer = getMediaId(input.listingPrimaryImage);
1723
- if (listingPointer && poolById.has(listingPointer)) {
1724
- return listingPointer;
1725
- }
1726
- const primaryPointer = toPointerId(input.productPrimaryMediaItemId);
1727
- if (primaryPointer && poolById.has(primaryPointer)) return primaryPointer;
1728
- const thumbnailPointer = getMediaId(input.productThumbnail);
1729
- if (thumbnailPointer && poolById.has(thumbnailPointer)) {
1730
- return thumbnailPointer;
1732
+ const featuredImagePointer = getMediaId(input.featuredImage);
1733
+ if (featuredImagePointer && poolById.has(featuredImagePointer)) {
1734
+ return featuredImagePointer;
1731
1735
  }
1732
1736
  if (pool.length > 0) {
1733
1737
  const firstPoolId = getMediaId(pool[0]);
@@ -1782,13 +1786,9 @@ function extractEntityId(value) {
1782
1786
  return null;
1783
1787
  }
1784
1788
  function resolveGenericListingPrimaryImage(product, resolvedPrimary, resolvedSource) {
1785
- return resolveListingPrimaryImagePointer({
1789
+ return resolveFeaturedImagePointer({
1786
1790
  productMediaPool: product?.images ?? [],
1787
- productPrimaryMediaItemId: getRelationID(
1788
- product?.primaryMediaItemId ?? null
1789
- ),
1790
- productThumbnail: product?.thumbnail ?? null,
1791
- listingPrimaryImage: product?.listing?.primaryImage ?? null,
1791
+ featuredImage: product?.featuredImage ?? null,
1792
1792
  resolvedPrimary,
1793
1793
  resolvedSource
1794
1794
  });
@@ -2390,11 +2390,14 @@ function compareVariantOrder(a, b) {
2390
2390
  const bId = String(getRelationID(b.id) ?? "");
2391
2391
  return aId.localeCompare(bId);
2392
2392
  }
2393
- function isVariantAvailableForSale(variant) {
2393
+ function getVariantAvailableForSale(variant) {
2394
2394
  if (variant.isActive === false) return false;
2395
2395
  if (variant.isUnlimited) return true;
2396
2396
  return (variant.stock ?? 0) - (variant.reservedStock ?? 0) > 0;
2397
2397
  }
2398
+ function isVariantAvailableForSale(variant) {
2399
+ return getVariantAvailableForSale(variant);
2400
+ }
2398
2401
  function sortVariantsForMediaSelection(variants) {
2399
2402
  const orderedVariants = [...variants].sort(compareVariantOrder);
2400
2403
  const activeVariants = orderedVariants.filter(
@@ -2427,7 +2430,7 @@ function getMinMax(values) {
2427
2430
  max: Math.max(...numbers)
2428
2431
  };
2429
2432
  }
2430
- function buildProductListingProjection(product, variants) {
2433
+ function buildProductListingGroupProjection(product, variants) {
2431
2434
  const orderedVariants = [...variants].sort(compareVariantOrder);
2432
2435
  const activeVariants = orderedVariants.filter(
2433
2436
  (variant) => variant.isActive !== false
@@ -2448,7 +2451,6 @@ function buildProductListingProjection(product, variants) {
2448
2451
  const mediaSelectionVariants = sortVariantsForMediaSelection(variants);
2449
2452
  const resolvedProductMedia = resolveProductSelectionMedia({
2450
2453
  productMediaPool,
2451
- productPrimaryMediaItemId: getRelationID(product?.primaryMediaItemId),
2452
2454
  selectedVariant: selectionHintVariant && selectionHintHasPoolMedia ? {
2453
2455
  id: selectionHintVariant.id,
2454
2456
  images: selectionHintVariant.images
@@ -2474,47 +2476,22 @@ function buildProductListingCard(item, options = {}) {
2474
2476
  const product = item.product;
2475
2477
  const groups = item.groups;
2476
2478
  const variants = getProductListingCardVariants(item);
2477
- const projectedListing = buildProductListingProjection(product, variants);
2478
- const selectionHintVariant = getRelationID(
2479
- product.listing && "selectionHintVariant" in product.listing ? product.listing.selectionHintVariant : null
2480
- ) ?? projectedListing.selectionHintVariant;
2479
+ const projectedListing = buildProductListingGroupProjection(product, variants);
2480
+ const selectionHintVariant = getRelationID(product.selectedOrFirstAvailableVariant) ?? projectedListing.selectionHintVariant;
2481
2481
  const representativeVariant = findListingCardRepresentativeVariant(
2482
2482
  variants,
2483
2483
  selectionHintVariant
2484
2484
  );
2485
2485
  const productMediaPool = mediaArray2(product.images);
2486
- const representativeHasPoolMedia = Boolean(
2487
- representativeVariant?.images?.some(
2488
- (image) => productMediaPool.some(
2489
- (poolItem) => getRelationID(poolItem) === getRelationID(image)
2490
- )
2491
- )
2492
- );
2493
- const resolvedPrimaryMedia = resolveProductSelectionMedia(
2494
- {
2495
- productMediaPool,
2496
- productPrimaryMediaItemId: getRelationID(product.primaryMediaItemId),
2497
- selectedVariant: representativeHasPoolMedia ? representativeVariant : null,
2498
- matchingVariants: representativeHasPoolMedia ? [] : variants
2499
- }
2500
- );
2501
- const listingPrimaryPointer = resolveListingPrimaryImagePointer({
2502
- productMediaPool,
2503
- productPrimaryMediaItemId: getRelationID(product.primaryMediaItemId),
2504
- productThumbnail: product.thumbnail ?? null,
2505
- listingPrimaryImage: product.listing?.primaryImage ?? null,
2506
- resolvedPrimary: resolvedPrimaryMedia.primaryImage,
2507
- resolvedSource: resolvedPrimaryMedia.source
2508
- });
2509
- const listingPrimaryMedia = listingPrimaryPointer ? productMediaPool.find(
2510
- (item2) => getRelationID(item2) === listingPrimaryPointer
2511
- ) ?? resolvedPrimaryMedia.primaryImage ?? null : null;
2486
+ const featuredImageId = getRelationID(product.featuredImage);
2487
+ const featuredImageFromPool = featuredImageId ? productMediaPool.find((item2) => getRelationID(item2) === featuredImageId) : void 0;
2488
+ const listingPrimaryMedia = featuredImageFromPool ?? product.featuredImage ?? productMediaPool[0] ?? null;
2512
2489
  const priceRange = resolveListingCardPriceRange(
2513
2490
  product,
2514
2491
  projectedListing,
2515
2492
  groups
2516
2493
  );
2517
- const availableForSale = product.listing?.availableForSale != null ? product.listing.availableForSale : groups.length > 0 ? groups.some((group) => group.listing.availableForSale) : projectedListing.availableForSale;
2494
+ const availableForSale = product.availableForSale != null ? product.availableForSale : groups.length > 0 ? groups.some((group) => group.listing.availableForSale) : projectedListing.availableForSale;
2518
2495
  const swatches = groups.length > 1 ? groups.map((group) => buildListingSwatch(product, group, options)) : [];
2519
2496
  return {
2520
2497
  id: String(product.id),
@@ -2554,23 +2531,20 @@ function findListingCardRepresentativeVariant(variants, representativeVariantId)
2554
2531
  (variant) => getRelationID(variant.id) === representativeVariantId
2555
2532
  ) ?? null;
2556
2533
  }
2557
- function hasCompleteListingPriceProjection(listing) {
2558
- return listing?.minPrice != null && listing?.maxPrice != null;
2559
- }
2560
- function listingDefinesCompareAtProjection(listing) {
2561
- return "minCompareAtPrice" in listing || "maxCompareAtPrice" in listing;
2534
+ function hasCompleteProductPriceRange(priceRange) {
2535
+ return priceRange?.minVariantPrice?.amount != null && priceRange?.maxVariantPrice?.amount != null;
2562
2536
  }
2563
2537
  function resolveListingCardPriceRange(product, projectedListing, groups) {
2564
- const listing = product.listing;
2565
- if (hasCompleteListingPriceProjection(listing)) {
2538
+ if (hasCompleteProductPriceRange(product.priceRange)) {
2566
2539
  const groupRange = groups.length > 0 ? aggregateListingPriceRange(groups) : null;
2567
- const definesCompareAt = listingDefinesCompareAtProjection(listing);
2540
+ const minCompareAtPrice = product.compareAtPriceRange?.minVariantPrice?.amount ?? groupRange?.minCompareAtPrice ?? null;
2541
+ const maxCompareAtPrice = product.compareAtPriceRange?.maxVariantPrice?.amount ?? groupRange?.maxCompareAtPrice ?? null;
2568
2542
  return {
2569
- minPrice: listing.minPrice,
2570
- maxPrice: listing.maxPrice,
2571
- minCompareAtPrice: definesCompareAt ? listing.minCompareAtPrice ?? null : groupRange?.minCompareAtPrice ?? projectedListing.minCompareAtPrice,
2572
- maxCompareAtPrice: definesCompareAt ? listing.maxCompareAtPrice ?? null : groupRange?.maxCompareAtPrice ?? projectedListing.maxCompareAtPrice,
2573
- isPriceRange: listing.isPriceRange ?? listing.minPrice !== listing.maxPrice
2543
+ minPrice: product.priceRange.minVariantPrice.amount,
2544
+ maxPrice: product.priceRange.maxVariantPrice.amount,
2545
+ minCompareAtPrice,
2546
+ maxCompareAtPrice,
2547
+ isPriceRange: product.priceRange.isPriceRange ?? product.priceRange.minVariantPrice.amount !== product.priceRange.maxVariantPrice.amount
2574
2548
  };
2575
2549
  }
2576
2550
  if (groups.length > 0) {
@@ -2682,15 +2656,19 @@ function buildProductListingPageWhere(params) {
2682
2656
  if (tagIds) clauses.push({ tags: { in: tagIds } });
2683
2657
  const minPrice = filters?.price?.min;
2684
2658
  if (minPrice != null) {
2685
- clauses.push({ "listing.maxPrice": { greater_than_equal: minPrice } });
2659
+ clauses.push({
2660
+ "priceRange.maxVariantPrice.amount": { greater_than_equal: minPrice }
2661
+ });
2686
2662
  }
2687
2663
  const maxPrice = filters?.price?.max;
2688
2664
  if (maxPrice != null) {
2689
- clauses.push({ "listing.minPrice": { less_than_equal: maxPrice } });
2665
+ clauses.push({
2666
+ "priceRange.minVariantPrice.amount": { less_than_equal: maxPrice }
2667
+ });
2690
2668
  }
2691
2669
  if (filters?.availableForSale != null) {
2692
2670
  clauses.push({
2693
- "listing.availableForSale": { equals: filters.availableForSale }
2671
+ availableForSale: { equals: filters.availableForSale }
2694
2672
  });
2695
2673
  }
2696
2674
  if (clauses.length === 0) return void 0;