@wocart/storefront 1.8.0 → 1.10.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 +24 -0
- package/USAGE.md +27 -2
- package/dist/client/collections.d.ts +18 -0
- package/dist/client/collections.js +29 -0
- package/dist/client/index.d.ts +3 -1
- package/dist/client/index.js +4 -1
- package/dist/domain.d.ts +2 -0
- package/dist/domain.js +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/offer-view.d.ts +18 -1
- package/dist/offer-view.js +21 -0
- package/dist/routes.d.ts +1 -0
- package/dist/routes.js +2 -0
- package/dist/schemas.d.ts +43 -0
- package/dist/schemas.js +28 -0
- package/dist/types.d.ts +14 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ follows [semantic versioning](https://semver.org/): breaking changes ship only
|
|
|
5
5
|
in a new major version, and an export is marked `@deprecated` for at least one
|
|
6
6
|
minor release before a major removes it.
|
|
7
7
|
|
|
8
|
+
## 1.10.0 — 2026-10-17
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Quantity pricing.** `ProductDetail.quantityPricing` (`QuantityPricing`,
|
|
13
|
+
`QuantityPricingSchema`): a product's price tiers by quantity, each with an
|
|
14
|
+
optional free delivery — null or absent when it has none.
|
|
15
|
+
`getQuantityTierViews(pricing, unitPrice)` projects them for tiles (price,
|
|
16
|
+
per-unit price, saving); the total is the server's cart quote.
|
|
17
|
+
- `CartValidationLine.offerId`: the quantity-pricing offer that priced the
|
|
18
|
+
line, whose `lineTotal` is then the tier's price. Optional.
|
|
19
|
+
|
|
20
|
+
## 1.9.0 — 2026-10-17
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- Page type `collection` at `/collections/:handle`: a collection's page,
|
|
25
|
+
which a merchant's menu can now link to. `storefrontPath("collection", {
|
|
26
|
+
handle })` builds it, `matchStorefrontPath` recognises it, and the WhatsApp
|
|
27
|
+
widget counts it as the catalog surface.
|
|
28
|
+
- `store.collections.get(handle)` → `StoreCollection` (handle, title,
|
|
29
|
+
description, in the client's language), `null` for an unknown handle, which
|
|
30
|
+
is not reported as a failure. `StoreCollectionSchema`.
|
|
31
|
+
|
|
8
32
|
## 1.8.0 — 2026-10-17
|
|
9
33
|
|
|
10
34
|
### Added
|
package/USAGE.md
CHANGED
|
@@ -105,6 +105,7 @@ break those links.
|
|
|
105
105
|
| `home` | `/` | the home page | required |
|
|
106
106
|
| `catalog` | `/products` | every product | required |
|
|
107
107
|
| `category` | `/category/:slug` | one category's products | required when the store has categories |
|
|
108
|
+
| `collection` | `/collections/:handle` | one collection's products, in the merchant's order | required — a menu can link to any collection |
|
|
108
109
|
| `product` | `/products/:handle` | one product (its public handle, never an id) | required |
|
|
109
110
|
| `page` | `/pages/:slug` | a legal or merchant page | required — checkout consent links to the store's legal pages |
|
|
110
111
|
| `landing` | `/lp/:slug` | a landing page | required when the store runs landing pages |
|
|
@@ -222,6 +223,7 @@ server directly.
|
|
|
222
223
|
| `productImageSource` | Which photo represents a product (cover → gallery fallback) |
|
|
223
224
|
| `getProductView`, `getProductListView` | Pure price, discount, default-variant, and stock values for detail and list projections |
|
|
224
225
|
| `getOfferTierView` | Pure offer-tier price, compare-price, savings, and free-shipping projection; display-only, never order authority |
|
|
226
|
+
| `getQuantityTierViews` | A product's quantity-pricing tiers as tiles show them — price, per-unit price, saving against the unit price, free delivery; display-only, the total is the server's cart quote |
|
|
225
227
|
| `META_CURRENCY`, `buildMetaProductPayload`, `buildMetaViewContentPayload`, `buildMetaAddToCartPayload`, `buildMetaInitiateCheckoutPayload` | Browser-safe Meta event payloads with the platform currency and catalog shape |
|
|
226
228
|
| `createMetaPixelTracker` | Injected browser-pixel dispatch; the theme supplies the vendor global and keeps bootstrap/timing |
|
|
227
229
|
| `createBasketStore`, `createLocalStorageAdapter`, `createMemoryStorageAdapter`, `DEFAULT_BASKET_STORAGE_KEY` | The browser basket, its storage, and the key it persists under (`cod_cart_v1`) |
|
|
@@ -624,8 +626,26 @@ if (!product) {
|
|
|
624
626
|
~~~
|
|
625
627
|
|
|
626
628
|
Product detail includes the product fields, parsed variant options and tags,
|
|
627
|
-
the category, variants, images, review statistics,
|
|
628
|
-
decides whether a product is publicly available.
|
|
629
|
+
the category, variants, images, review statistics, quantity pricing, and
|
|
630
|
+
offers. The server decides whether a product is publicly available.
|
|
631
|
+
|
|
632
|
+
**Quantity pricing** (`product.quantityPricing`, null when the product has
|
|
633
|
+
none) prices a product by how many are bought — "1 for 2,000 DA, 2 for 3,500
|
|
634
|
+
DA, 3 for 4,500 DA". The highest tier the quantity reaches prices every unit
|
|
635
|
+
at its per-unit price, and a tier may include free delivery for a basket of
|
|
636
|
+
that product alone. A product has at most one offer per order, so it has
|
|
637
|
+
either quantity pricing or offers in `offers`, never both at once.
|
|
638
|
+
|
|
639
|
+
~~~ts
|
|
640
|
+
const tiles = product.quantityPricing ? getQuantityTierViews(product.quantityPricing, product.price) : [];
|
|
641
|
+
// tiles: [{ quantity, price, perUnit, savingsAmount, savePct, freeShipping }]
|
|
642
|
+
~~~
|
|
643
|
+
|
|
644
|
+
Show the tiers as choices; never compute the total yourself. The cart quote
|
|
645
|
+
(`POST /api/cart/validate`) answers what the shopper pays — each line's
|
|
646
|
+
`lineTotal` already priced by its tier (`offerId` names the offer), and the
|
|
647
|
+
delivery — and the order charges exactly that. A tier locked to one variant
|
|
648
|
+
(`triggerVariantId`) counts and prices only that variant.
|
|
629
649
|
|
|
630
650
|
### Product lists
|
|
631
651
|
|
|
@@ -651,6 +671,11 @@ const onSale = await store.products.list({ onSale: true, limit: 8 });
|
|
|
651
671
|
- An empty or unknown collection lists nothing — show nothing, never the
|
|
652
672
|
whole catalog instead: "Best sellers" means the merchant's picks.
|
|
653
673
|
- `onSale` is computed: a product priced below its compare-at price.
|
|
674
|
+
- A collection has a page of its own (page type `collection`,
|
|
675
|
+
`/collections/:handle`) that a merchant's menu can link to. Name it with
|
|
676
|
+
`store.collections.get(handle)` — title and description in the client's
|
|
677
|
+
language, `null` for an unknown handle (answer a real 404) — and list it
|
|
678
|
+
with `products.list({ collection: handle })`.
|
|
654
679
|
- `featured: true` and `ProductList.storeFeatured` are deprecated: they mean
|
|
655
680
|
the `featured` collection.
|
|
656
681
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collections Namespace - the merchant's curated product lists
|
|
3
|
+
*
|
|
4
|
+
* A collection has a page of its own (`/collections/:handle`, page type
|
|
5
|
+
* `collection`): `get(handle)` names it — title and description in the
|
|
6
|
+
* client's language — and `products.list({ collection })` lists its products
|
|
7
|
+
* in the merchant's order.
|
|
8
|
+
*
|
|
9
|
+
* Failure contract: `get(handle)` → null means "no such collection" or a
|
|
10
|
+
* failed read — the page answers a real 404.
|
|
11
|
+
*/
|
|
12
|
+
import { type ClientCore } from "./core.js";
|
|
13
|
+
import type { StoreCollection } from "../types.js";
|
|
14
|
+
export interface CollectionsNamespace {
|
|
15
|
+
/** A collection by handle (`best-sellers`). Resolves to `null` for an unknown handle or a failed read. */
|
|
16
|
+
get(handle: string): Promise<StoreCollection | null>;
|
|
17
|
+
}
|
|
18
|
+
export declare function createCollectionsNamespace(core: ClientCore): CollectionsNamespace;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collections Namespace - the merchant's curated product lists
|
|
3
|
+
*
|
|
4
|
+
* A collection has a page of its own (`/collections/:handle`, page type
|
|
5
|
+
* `collection`): `get(handle)` names it — title and description in the
|
|
6
|
+
* client's language — and `products.list({ collection })` lists its products
|
|
7
|
+
* in the merchant's order.
|
|
8
|
+
*
|
|
9
|
+
* Failure contract: `get(handle)` → null means "no such collection" or a
|
|
10
|
+
* failed read — the page answers a real 404.
|
|
11
|
+
*/
|
|
12
|
+
import { StoreCollectionSchema } from "../schemas.js";
|
|
13
|
+
import { STOREFRONT_OPERATIONS } from "../domain.js";
|
|
14
|
+
import { localizedPath } from "./paths.js";
|
|
15
|
+
import { isNotFoundAnswer, reportDegradedRead } from "./core.js";
|
|
16
|
+
export function createCollectionsNamespace(core) {
|
|
17
|
+
return {
|
|
18
|
+
async get(handle) {
|
|
19
|
+
try {
|
|
20
|
+
return await core.get(localizedPath(core.config, `/store/collections/${encodeURIComponent(handle)}`), StoreCollectionSchema, { operation: STOREFRONT_OPERATIONS.collectionsGet });
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (!isNotFoundAnswer(error))
|
|
24
|
+
reportDegradedRead(core.config, error, "collections.get");
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Store Client Factory — the SDK's front door.
|
|
3
3
|
*
|
|
4
4
|
* One server-side call builds the whole read/write surface a theme needs, over one
|
|
5
|
-
* shared core: config, products, categories, pages, menus,
|
|
5
|
+
* shared core: config, products, categories, collections, pages, menus,
|
|
6
|
+
* shipping, geo, cart,
|
|
6
7
|
* orders. The core is internal — consumers see only namespaces.
|
|
7
8
|
*
|
|
8
9
|
* One core means one connection story for every namespace: every read is
|
|
@@ -32,6 +33,7 @@ export declare function createStoreClient(config: StoreScope): {
|
|
|
32
33
|
categories: import("./categories.js").CategoriesNamespace;
|
|
33
34
|
pages: import("./pages.js").PagesNamespace;
|
|
34
35
|
menus: import("./menus.js").MenusNamespace;
|
|
36
|
+
collections: import("./collections.js").CollectionsNamespace;
|
|
35
37
|
pageData: import("./page-data.js").PageDataNamespace;
|
|
36
38
|
shipping: import("./shipping.js").ShippingNamespace;
|
|
37
39
|
geo: import("./geo.js").GeoNamespace;
|
package/dist/client/index.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Store Client Factory — the SDK's front door.
|
|
3
3
|
*
|
|
4
4
|
* One server-side call builds the whole read/write surface a theme needs, over one
|
|
5
|
-
* shared core: config, products, categories, pages, menus,
|
|
5
|
+
* shared core: config, products, categories, collections, pages, menus,
|
|
6
|
+
* shipping, geo, cart,
|
|
6
7
|
* orders. The core is internal — consumers see only namespaces.
|
|
7
8
|
*
|
|
8
9
|
* One core means one connection story for every namespace: every read is
|
|
@@ -32,6 +33,7 @@ import { createProductsNamespace } from "./products.js";
|
|
|
32
33
|
import { createCategoriesNamespace } from "./categories.js";
|
|
33
34
|
import { createPagesNamespace } from "./pages.js";
|
|
34
35
|
import { createMenusNamespace } from "./menus.js";
|
|
36
|
+
import { createCollectionsNamespace } from "./collections.js";
|
|
35
37
|
import { createPageDataNamespace } from "./page-data.js";
|
|
36
38
|
import { createShippingNamespace } from "./shipping.js";
|
|
37
39
|
import { createGeoNamespace } from "./geo.js";
|
|
@@ -46,6 +48,7 @@ export function createStoreClient(config) {
|
|
|
46
48
|
categories: createCategoriesNamespace(core),
|
|
47
49
|
pages: createPagesNamespace(core),
|
|
48
50
|
menus: createMenusNamespace(core),
|
|
51
|
+
collections: createCollectionsNamespace(core),
|
|
49
52
|
pageData: createPageDataNamespace(core),
|
|
50
53
|
shipping: createShippingNamespace(core),
|
|
51
54
|
geo: createGeoNamespace(core),
|
package/dist/domain.d.ts
CHANGED
|
@@ -354,6 +354,7 @@ export declare const STOREFRONT_OPERATIONS: {
|
|
|
354
354
|
readonly pagesGet: "pages.get";
|
|
355
355
|
readonly pagesLanding: "pages.landing";
|
|
356
356
|
readonly menusGet: "menus.get";
|
|
357
|
+
readonly collectionsGet: "collections.get";
|
|
357
358
|
readonly shippingRates: "shipping.rates";
|
|
358
359
|
readonly shippingRate: "shipping.rate";
|
|
359
360
|
readonly geoCommunes: "geo.communes";
|
|
@@ -392,6 +393,7 @@ export declare const STOREFRONT_RETRY_POLICIES: {
|
|
|
392
393
|
readonly "pages.get": "safe-read";
|
|
393
394
|
readonly "pages.landing": "safe-read";
|
|
394
395
|
readonly "menus.get": "safe-read";
|
|
396
|
+
readonly "collections.get": "safe-read";
|
|
395
397
|
readonly "products.get": "safe-read";
|
|
396
398
|
readonly "products.list": "safe-read";
|
|
397
399
|
readonly "products.reviews.list": "safe-read";
|
package/dist/domain.js
CHANGED
|
@@ -207,6 +207,7 @@ export const STOREFRONT_OPERATIONS = {
|
|
|
207
207
|
pagesGet: "pages.get",
|
|
208
208
|
pagesLanding: "pages.landing",
|
|
209
209
|
menusGet: "menus.get",
|
|
210
|
+
collectionsGet: "collections.get",
|
|
210
211
|
shippingRates: "shipping.rates",
|
|
211
212
|
shippingRate: "shipping.rate",
|
|
212
213
|
geoCommunes: "geo.communes",
|
|
@@ -244,6 +245,7 @@ export const STOREFRONT_RETRY_POLICIES = {
|
|
|
244
245
|
[STOREFRONT_OPERATIONS.pagesGet]: "safe-read",
|
|
245
246
|
[STOREFRONT_OPERATIONS.pagesLanding]: "safe-read",
|
|
246
247
|
[STOREFRONT_OPERATIONS.menusGet]: "safe-read",
|
|
248
|
+
[STOREFRONT_OPERATIONS.collectionsGet]: "safe-read",
|
|
247
249
|
[STOREFRONT_OPERATIONS.productsGet]: "safe-read",
|
|
248
250
|
[STOREFRONT_OPERATIONS.productsList]: "safe-read",
|
|
249
251
|
[STOREFRONT_OPERATIONS.productsReviewsList]: "safe-read",
|
package/dist/index.d.ts
CHANGED
|
@@ -34,8 +34,8 @@ export * from "./types.js";
|
|
|
34
34
|
export * from "./schemas.js";
|
|
35
35
|
export * from "./domain.js";
|
|
36
36
|
export * from "./product-view.js";
|
|
37
|
-
export { getOfferTierView } from "./offer-view.js";
|
|
38
|
-
export type { OfferTierInput, OfferTierView } from "./offer-view.js";
|
|
37
|
+
export { getOfferTierView, getQuantityTierViews } from "./offer-view.js";
|
|
38
|
+
export type { OfferTierInput, OfferTierView, QuantityTierView } from "./offer-view.js";
|
|
39
39
|
export { createAbandonedCheckoutTracker } from "./abandoned-checkout.js";
|
|
40
40
|
export type { AbandonedCheckoutPorts, AbandonedCheckoutSnapshot, AbandonedCheckoutStorage, AbandonedCheckoutTracker, } from "./abandoned-checkout.js";
|
|
41
41
|
export { META_CURRENCY, buildMetaAddToCartPayload, buildMetaInitiateCheckoutPayload, buildMetaProductPayload, buildMetaViewContentPayload, createMetaPixelTracker, } from "./meta-tracking.js";
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,6 @@ export * from "./types.js";
|
|
|
25
25
|
export * from "./schemas.js";
|
|
26
26
|
export * from "./domain.js";
|
|
27
27
|
export * from "./product-view.js";
|
|
28
|
-
export { getOfferTierView } from "./offer-view.js";
|
|
28
|
+
export { getOfferTierView, getQuantityTierViews } from "./offer-view.js";
|
|
29
29
|
export { createAbandonedCheckoutTracker } from "./abandoned-checkout.js";
|
|
30
30
|
export { META_CURRENCY, buildMetaAddToCartPayload, buildMetaInitiateCheckoutPayload, buildMetaProductPayload, buildMetaViewContentPayload, createMetaPixelTracker, } from "./meta-tracking.js";
|
package/dist/offer-view.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Offer } from "./types.js";
|
|
1
|
+
import type { Offer, QuantityPricing } from "./types.js";
|
|
2
2
|
export interface OfferTierView {
|
|
3
3
|
isFreeShipping: boolean;
|
|
4
4
|
savePct: number;
|
|
@@ -8,3 +8,20 @@ export interface OfferTierView {
|
|
|
8
8
|
}
|
|
9
9
|
export type OfferTierInput = Pick<Offer, "discountType" | "triggerQuantity" | "rewardQuantity" | "rewardUnitPrice">;
|
|
10
10
|
export declare function getOfferTierView(offer: OfferTierInput, basePrice: number, comparePrice: number | null): OfferTierView;
|
|
11
|
+
export interface QuantityTierView {
|
|
12
|
+
quantity: number;
|
|
13
|
+
/** What that many units cost. */
|
|
14
|
+
price: number;
|
|
15
|
+
/** What one unit costs within the tier, rounded to the dinar. */
|
|
16
|
+
perUnit: number;
|
|
17
|
+
/** What the tier saves against buying that many at `unitPrice`; 0 when it saves nothing. */
|
|
18
|
+
savingsAmount: number;
|
|
19
|
+
savePct: number;
|
|
20
|
+
freeShipping: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The tiers of a product's quantity pricing as tiles show them: price, per-unit
|
|
24
|
+
* price and saving against the product's unit price. Presentation only — the
|
|
25
|
+
* total for a quantity is the server's cart quote.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getQuantityTierViews(pricing: Pick<QuantityPricing, "tiers">, unitPrice: number): QuantityTierView[];
|
package/dist/offer-view.js
CHANGED
|
@@ -15,3 +15,24 @@ export function getOfferTierView(offer, basePrice, comparePrice) {
|
|
|
15
15
|
comparePrice: comparePrice === null ? null : comparePrice * offer.triggerQuantity,
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* The tiers of a product's quantity pricing as tiles show them: price, per-unit
|
|
20
|
+
* price and saving against the product's unit price. Presentation only — the
|
|
21
|
+
* total for a quantity is the server's cart quote.
|
|
22
|
+
*/
|
|
23
|
+
export function getQuantityTierViews(pricing, unitPrice) {
|
|
24
|
+
return [...pricing.tiers]
|
|
25
|
+
.sort((a, b) => a.quantity - b.quantity)
|
|
26
|
+
.map((tier) => {
|
|
27
|
+
const listValue = unitPrice * tier.quantity;
|
|
28
|
+
const savingsAmount = Math.max(0, listValue - tier.price);
|
|
29
|
+
return {
|
|
30
|
+
quantity: tier.quantity,
|
|
31
|
+
price: tier.price,
|
|
32
|
+
perUnit: Math.round(tier.price / tier.quantity),
|
|
33
|
+
savingsAmount,
|
|
34
|
+
savePct: listValue > 0 ? Math.round((savingsAmount / listValue) * 100) : 0,
|
|
35
|
+
freeShipping: tier.freeShipping,
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
package/dist/routes.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export declare const STOREFRONT_PAGE_PATHS: {
|
|
|
20
20
|
readonly home: "/";
|
|
21
21
|
readonly catalog: "/products";
|
|
22
22
|
readonly category: "/category/:slug";
|
|
23
|
+
readonly collection: "/collections/:handle";
|
|
23
24
|
readonly product: "/products/:handle";
|
|
24
25
|
readonly page: "/pages/:slug";
|
|
25
26
|
readonly landing: "/lp/:slug";
|
package/dist/routes.js
CHANGED
|
@@ -3,6 +3,7 @@ export const STOREFRONT_PAGE_PATHS = {
|
|
|
3
3
|
home: "/",
|
|
4
4
|
catalog: "/products",
|
|
5
5
|
category: "/category/:slug",
|
|
6
|
+
collection: "/collections/:handle",
|
|
6
7
|
product: "/products/:handle",
|
|
7
8
|
page: "/pages/:slug",
|
|
8
9
|
landing: "/lp/:slug",
|
|
@@ -58,6 +59,7 @@ const WIDGET_SURFACE = {
|
|
|
58
59
|
home: "home",
|
|
59
60
|
catalog: "catalog",
|
|
60
61
|
category: "catalog",
|
|
62
|
+
collection: "catalog",
|
|
61
63
|
product: "product",
|
|
62
64
|
page: "pages",
|
|
63
65
|
landing: "landing",
|
package/dist/schemas.d.ts
CHANGED
|
@@ -43,6 +43,21 @@ export declare const OfferSchema: z.ZodObject<{
|
|
|
43
43
|
rewardVariantLabel: z.ZodNullable<z.ZodString>;
|
|
44
44
|
rewardUnitPrice: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
45
45
|
}, z.core.$strip>;
|
|
46
|
+
/**
|
|
47
|
+
* Quantity pricing — price tiers by quantity ("2 for 3,500 DA"). The highest
|
|
48
|
+
* tier the quantity reaches prices every unit at its per-unit price; the
|
|
49
|
+
* server's cart quote is the total.
|
|
50
|
+
*/
|
|
51
|
+
export declare const QuantityPricingSchema: z.ZodObject<{
|
|
52
|
+
offerId: z.ZodString;
|
|
53
|
+
name: z.ZodString;
|
|
54
|
+
triggerVariantId: z.ZodNullable<z.ZodString>;
|
|
55
|
+
tiers: z.ZodArray<z.ZodObject<{
|
|
56
|
+
quantity: z.ZodNumber;
|
|
57
|
+
price: z.ZodNumber;
|
|
58
|
+
freeShipping: z.ZodBoolean;
|
|
59
|
+
}, z.core.$strip>>;
|
|
60
|
+
}, z.core.$strip>;
|
|
46
61
|
export declare const CategorySchema: z.ZodObject<{
|
|
47
62
|
id: z.ZodString;
|
|
48
63
|
name: z.ZodString;
|
|
@@ -201,6 +216,16 @@ export declare const ProductSchema: z.ZodObject<{
|
|
|
201
216
|
avgRating: z.ZodNumber;
|
|
202
217
|
reviewCount: z.ZodNumber;
|
|
203
218
|
}, z.core.$strip>>;
|
|
219
|
+
quantityPricing: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
220
|
+
offerId: z.ZodString;
|
|
221
|
+
name: z.ZodString;
|
|
222
|
+
triggerVariantId: z.ZodNullable<z.ZodString>;
|
|
223
|
+
tiers: z.ZodArray<z.ZodObject<{
|
|
224
|
+
quantity: z.ZodNumber;
|
|
225
|
+
price: z.ZodNumber;
|
|
226
|
+
freeShipping: z.ZodBoolean;
|
|
227
|
+
}, z.core.$strip>>;
|
|
228
|
+
}, z.core.$strip>>>;
|
|
204
229
|
offers: z.ZodArray<z.ZodObject<{
|
|
205
230
|
id: z.ZodString;
|
|
206
231
|
name: z.ZodString;
|
|
@@ -246,6 +271,12 @@ interface StoreMenuItemShape {
|
|
|
246
271
|
type: string;
|
|
247
272
|
children: StoreMenuItemShape[];
|
|
248
273
|
}
|
|
274
|
+
/** A collection's page — GET /store/collections/{handle}. Its products come from `products.list({ collection })`. */
|
|
275
|
+
export declare const StoreCollectionSchema: z.ZodObject<{
|
|
276
|
+
handle: z.ZodString;
|
|
277
|
+
title: z.ZodString;
|
|
278
|
+
description: z.ZodNullable<z.ZodString>;
|
|
279
|
+
}, z.core.$strip>;
|
|
249
280
|
export declare const StoreMenuSchema: z.ZodObject<{
|
|
250
281
|
handle: z.ZodString;
|
|
251
282
|
title: z.ZodString;
|
|
@@ -623,6 +654,16 @@ export declare const LandingPageSchema: z.ZodObject<{
|
|
|
623
654
|
avgRating: z.ZodNumber;
|
|
624
655
|
reviewCount: z.ZodNumber;
|
|
625
656
|
}, z.core.$strip>>;
|
|
657
|
+
quantityPricing: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
658
|
+
offerId: z.ZodString;
|
|
659
|
+
name: z.ZodString;
|
|
660
|
+
triggerVariantId: z.ZodNullable<z.ZodString>;
|
|
661
|
+
tiers: z.ZodArray<z.ZodObject<{
|
|
662
|
+
quantity: z.ZodNumber;
|
|
663
|
+
price: z.ZodNumber;
|
|
664
|
+
freeShipping: z.ZodBoolean;
|
|
665
|
+
}, z.core.$strip>>;
|
|
666
|
+
}, z.core.$strip>>>;
|
|
626
667
|
offers: z.ZodArray<z.ZodObject<{
|
|
627
668
|
id: z.ZodString;
|
|
628
669
|
name: z.ZodString;
|
|
@@ -671,6 +712,7 @@ export declare const CartValidationLineSchema: z.ZodObject<{
|
|
|
671
712
|
quantity: z.ZodNumber;
|
|
672
713
|
unitPrice: z.ZodNumber;
|
|
673
714
|
lineTotal: z.ZodNumber;
|
|
715
|
+
offerId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
674
716
|
maxQuantity: z.ZodNullable<z.ZodNumber>;
|
|
675
717
|
blocker: z.ZodNullable<z.ZodEnum<{
|
|
676
718
|
missing: "missing";
|
|
@@ -693,6 +735,7 @@ export declare const CartValidationSchema: z.ZodObject<{
|
|
|
693
735
|
quantity: z.ZodNumber;
|
|
694
736
|
unitPrice: z.ZodNumber;
|
|
695
737
|
lineTotal: z.ZodNumber;
|
|
738
|
+
offerId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
696
739
|
maxQuantity: z.ZodNullable<z.ZodNumber>;
|
|
697
740
|
blocker: z.ZodNullable<z.ZodEnum<{
|
|
698
741
|
missing: "missing";
|
package/dist/schemas.js
CHANGED
|
@@ -42,6 +42,21 @@ export const OfferSchema = z.object({
|
|
|
42
42
|
// it omits the field, and the product's own price stands in for the gift.
|
|
43
43
|
rewardUnitPrice: z.number().nullish(),
|
|
44
44
|
});
|
|
45
|
+
/**
|
|
46
|
+
* Quantity pricing — price tiers by quantity ("2 for 3,500 DA"). The highest
|
|
47
|
+
* tier the quantity reaches prices every unit at its per-unit price; the
|
|
48
|
+
* server's cart quote is the total.
|
|
49
|
+
*/
|
|
50
|
+
export const QuantityPricingSchema = z.object({
|
|
51
|
+
offerId: z.string(),
|
|
52
|
+
name: z.string(),
|
|
53
|
+
triggerVariantId: z.string().nullable(),
|
|
54
|
+
tiers: z.array(z.object({
|
|
55
|
+
quantity: z.number().int(),
|
|
56
|
+
price: z.number().int(),
|
|
57
|
+
freeShipping: z.boolean(),
|
|
58
|
+
})),
|
|
59
|
+
});
|
|
45
60
|
const productCategorySchema = z.object({
|
|
46
61
|
id: z.string(),
|
|
47
62
|
name: z.string(),
|
|
@@ -122,6 +137,11 @@ export const ProductSchema = z.object({
|
|
|
122
137
|
variants: z.array(ProductVariantSchema),
|
|
123
138
|
images: z.array(ProductImageSchema),
|
|
124
139
|
reviewStats: reviewStatsSchema.nullable(),
|
|
140
|
+
/**
|
|
141
|
+
* The product's live quantity pricing; null or absent when it has none
|
|
142
|
+
* (absent from a server that predates it).
|
|
143
|
+
*/
|
|
144
|
+
quantityPricing: QuantityPricingSchema.nullish(),
|
|
125
145
|
offers: z.array(OfferSchema),
|
|
126
146
|
});
|
|
127
147
|
export const StorePagePublicSchema = z.object({
|
|
@@ -145,6 +165,12 @@ export const StoreMenuItemSchema = z.lazy(() => z.object({
|
|
|
145
165
|
type: z.string(),
|
|
146
166
|
children: z.array(StoreMenuItemSchema),
|
|
147
167
|
}));
|
|
168
|
+
/** A collection's page — GET /store/collections/{handle}. Its products come from `products.list({ collection })`. */
|
|
169
|
+
export const StoreCollectionSchema = z.object({
|
|
170
|
+
handle: z.string(),
|
|
171
|
+
title: z.string(),
|
|
172
|
+
description: z.string().nullable(),
|
|
173
|
+
});
|
|
148
174
|
export const StoreMenuSchema = z.object({
|
|
149
175
|
handle: z.string(),
|
|
150
176
|
title: z.string(),
|
|
@@ -325,6 +351,8 @@ export const CartValidationLineSchema = z.object({
|
|
|
325
351
|
quantity: z.number().int(),
|
|
326
352
|
unitPrice: z.number(),
|
|
327
353
|
lineTotal: z.number(),
|
|
354
|
+
/** The quantity-pricing offer that set `lineTotal`; absent from a server that predates offers on lines. */
|
|
355
|
+
offerId: z.string().nullish(),
|
|
328
356
|
maxQuantity: z.number().nullable(),
|
|
329
357
|
blocker: z.enum(["missing", "unlisted", "out_of_stock"]).nullable(),
|
|
330
358
|
});
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { ProductSchema, ProductListSchema, CategorySchema, ProductImageSchema, ProductVariantSchema, OfferSchema, PageDocumentPayloadSchema } from "./schemas.js";
|
|
2
|
+
import { ProductSchema, ProductListSchema, CategorySchema, ProductImageSchema, ProductVariantSchema, OfferSchema, QuantityPricingSchema, PageDocumentPayloadSchema } from "./schemas.js";
|
|
3
3
|
export type Product = z.infer<typeof ProductSchema>;
|
|
4
4
|
export type ProductList = z.infer<typeof ProductListSchema>;
|
|
5
5
|
/** Compact catalog projection used in grids, cards and search results. */
|
|
@@ -10,6 +10,9 @@ export type Category = z.infer<typeof CategorySchema>;
|
|
|
10
10
|
export type ProductImage = z.infer<typeof ProductImageSchema>;
|
|
11
11
|
export type ProductVariant = z.infer<typeof ProductVariantSchema>;
|
|
12
12
|
export type Offer = z.infer<typeof OfferSchema>;
|
|
13
|
+
/** A product's quantity pricing — `ProductDetail.quantityPricing`. */
|
|
14
|
+
export type QuantityPricing = z.infer<typeof QuantityPricingSchema>;
|
|
15
|
+
export type QuantityPricingTier = QuantityPricing["tiers"][number];
|
|
13
16
|
/**
|
|
14
17
|
* One store's saved composition document for one surface. Transport shape
|
|
15
18
|
* only — `sections` items are `unknown`; the theme
|
|
@@ -212,6 +215,13 @@ export interface StorePagePublic {
|
|
|
212
215
|
metaTitle: string | null;
|
|
213
216
|
metaDescription: string | null;
|
|
214
217
|
}
|
|
218
|
+
/** A collection as its page names it — GET /store/collections/{handle}. */
|
|
219
|
+
export interface StoreCollection {
|
|
220
|
+
handle: string;
|
|
221
|
+
/** In the client's language when the store translated it, else the primary. */
|
|
222
|
+
title: string;
|
|
223
|
+
description: string | null;
|
|
224
|
+
}
|
|
215
225
|
/** A navigation menu — GET /store/menus/{handle}. */
|
|
216
226
|
export interface StoreMenu {
|
|
217
227
|
handle: string;
|
|
@@ -300,7 +310,10 @@ export interface CartValidationLine {
|
|
|
300
310
|
quantity: number;
|
|
301
311
|
/** CURRENT catalog price — the cart's optimistic price is display-only. */
|
|
302
312
|
unitPrice: number;
|
|
313
|
+
/** What the line costs: unit price × quantity, or its quantity-pricing tier's price. */
|
|
303
314
|
lineTotal: number;
|
|
315
|
+
/** The quantity-pricing offer that set `lineTotal`; null or absent at the catalog price. */
|
|
316
|
+
offerId?: string | null | undefined;
|
|
304
317
|
/** Units still available; null when the product is not stock-tracked. */
|
|
305
318
|
maxQuantity: number | null;
|
|
306
319
|
/**
|
package/package.json
CHANGED