@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.
- package/CHANGELOG.md +492 -0
- package/MIGRATION.md +183 -0
- package/README.md +64 -21
- package/dist/client.cjs +47 -69
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +6 -6
- package/dist/client.d.ts +6 -6
- package/dist/client.js +47 -69
- package/dist/client.js.map +1 -1
- package/dist/{collection-client-DVfB0Em1.d.cts → collection-client-Bymfni8u.d.cts} +3 -3
- package/dist/{collection-client-CaMgs5KE.d.ts → collection-client-CN4lj6gi.d.ts} +3 -3
- package/dist/const-4BUtUdGU.d.cts +32 -0
- package/dist/const-ymprfiPf.d.ts +32 -0
- package/dist/{index-BOLQxveo.d.cts → index-Dquv4xTL.d.cts} +3 -3
- package/dist/{index-CSwR2HSg.d.ts → index-w36lpSkU.d.ts} +3 -3
- package/dist/index.cjs +119 -136
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +119 -136
- package/dist/index.js.map +1 -1
- package/dist/{payload-types-m3jjhxk9.d.cts → payload-types-DC0xzR9i.d.cts} +470 -265
- package/dist/{payload-types-m3jjhxk9.d.ts → payload-types-DC0xzR9i.d.ts} +470 -265
- package/dist/query.d.cts +5 -5
- package/dist/query.d.ts +5 -5
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/server.cjs +40 -69
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +7 -7
- package/dist/server.d.ts +7 -7
- package/dist/server.js +40 -69
- package/dist/server.js.map +1 -1
- package/dist/{types-Cmrd1ezc.d.ts → types-Bh2p-EMc.d.ts} +5 -1
- package/dist/{types-CVf8sCZ-.d.ts → types-Bl-m9ttd.d.cts} +129 -77
- package/dist/{types-D0ubzQw0.d.cts → types-BvpjooU-.d.cts} +5 -1
- package/dist/{types-BQo7UdI9.d.cts → types-CHrzs2GB.d.ts} +129 -77
- package/dist/ui/form.d.cts +1 -1
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/video.d.cts +1 -1
- package/dist/ui/video.d.ts +1 -1
- package/dist/webhook.d.cts +4 -4
- package/dist/webhook.d.ts +4 -4
- package/package.json +6 -4
- package/dist/const-6XHz_jej.d.ts +0 -32
- 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
|
-
|
|
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,
|
|
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 {
|
|
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: {
|
|
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
|
-
`
|
|
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
|
|
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
|
|
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
|
|
552
|
-
`product.
|
|
553
|
-
|
|
554
|
-
groups when there is more than one. Single-group
|
|
555
|
-
storefronts that disagree can read `item.groups`
|
|
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
|
|
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
|
-
|
|
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`:
|
|
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
|
|
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
|
-
|
|
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`
|
|
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
|
|
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
|
|
1723
|
-
if (
|
|
1724
|
-
return
|
|
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
|
|
1789
|
+
return resolveFeaturedImagePointer({
|
|
1786
1790
|
productMediaPool: product?.images ?? [],
|
|
1787
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
2487
|
-
|
|
2488
|
-
|
|
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.
|
|
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
|
|
2558
|
-
return
|
|
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
|
-
|
|
2565
|
-
if (hasCompleteListingPriceProjection(listing)) {
|
|
2538
|
+
if (hasCompleteProductPriceRange(product.priceRange)) {
|
|
2566
2539
|
const groupRange = groups.length > 0 ? aggregateListingPriceRange(groups) : null;
|
|
2567
|
-
const
|
|
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:
|
|
2570
|
-
maxPrice:
|
|
2571
|
-
minCompareAtPrice
|
|
2572
|
-
maxCompareAtPrice
|
|
2573
|
-
isPriceRange:
|
|
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({
|
|
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({
|
|
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
|
-
|
|
2671
|
+
availableForSale: { equals: filters.availableForSale }
|
|
2694
2672
|
});
|
|
2695
2673
|
}
|
|
2696
2674
|
if (clauses.length === 0) return void 0;
|