@cartbase/storefront 0.17.0 → 0.18.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/LICENSE +21 -21
- package/package.json +258 -256
- package/src/api/auth.ts +108 -108
- package/src/api/carts.ts +523 -523
- package/src/api/categories.ts +184 -184
- package/src/api/checkout.ts +526 -526
- package/src/api/collections.ts +130 -130
- package/src/api/consent.ts +75 -75
- package/src/api/content.ts +125 -125
- package/src/api/customers.ts +303 -303
- package/src/api/gift-cards.ts +112 -112
- package/src/api/http.ts +180 -180
- package/src/api/index.ts +30 -30
- package/src/api/menus.ts +77 -77
- package/src/api/metaobjects.ts +136 -136
- package/src/api/orders.ts +290 -290
- package/src/api/products.ts +43 -1
- package/src/api/redirects.ts +37 -37
- package/src/api/regions.ts +200 -200
- package/src/api/reviews.ts +259 -259
- package/src/api/search.ts +163 -163
- package/src/api/store.ts +35 -35
- package/src/api/types.ts +91 -91
- package/src/cart-drawer/context.tsx +277 -164
- package/src/cart-drawer/cross-sell-carousel.tsx +211 -211
- package/src/cart-drawer/cross-sell-sidebar.tsx +158 -158
- package/src/cart-drawer/gift-wrap.tsx +82 -82
- package/src/cart-drawer/index.ts +71 -68
- package/src/cart-drawer/item/index.tsx +162 -162
- package/src/cart-drawer/item/quantity.tsx +8 -14
- package/src/cart-drawer/item/upsell.tsx +110 -110
- package/src/cart-drawer/labels.ts +123 -123
- package/src/cart-drawer/mutation-queue.ts +78 -0
- package/src/cart-drawer/notes.tsx +131 -131
- package/src/cart-drawer/sticky-footer.tsx +73 -73
- package/src/cart-drawer/summary-breakdown.tsx +197 -197
- package/src/checkout/address-error-copy.ts +117 -117
- package/src/checkout/boxnow-locker-selector.tsx +410 -410
- package/src/checkout/compare-addresses.ts +40 -40
- package/src/checkout/discount-section.tsx +218 -218
- package/src/checkout/error-copy-codes.ts +63 -63
- package/src/checkout/geocode.ts +154 -154
- package/src/checkout/gift-card-section.tsx +224 -224
- package/src/checkout/index.ts +72 -72
- package/src/checkout/labels.ts +495 -495
- package/src/checkout/payment-button.tsx +372 -372
- package/src/checkout/payment-error-copy.ts +154 -154
- package/src/checkout/promotion-error-copy.ts +91 -91
- package/src/checkout/use-checkout-orchestration.ts +1592 -1592
- package/src/common/country-flag.tsx +52 -52
- package/src/common/country-select.tsx +11 -11
- package/src/common/index.ts +20 -20
- package/src/common/market-select.tsx +57 -57
- package/src/index.ts +12 -12
- package/src/lib/country-name.ts +59 -59
- package/src/lib/get-product-price.ts +133 -133
- package/src/lib/media-image.tsx +39 -39
- package/src/lib/payment-constants.ts +53 -53
- package/src/lib/platform.ts +13 -13
- package/src/lib/price.tsx +39 -39
- package/src/lib/store-api-error.ts +36 -36
- package/src/lib/variant-caption.ts +32 -32
- package/src/locales/bg.ts +467 -467
- package/src/locales/context.ts +37 -37
- package/src/locales/en.ts +26 -26
- package/src/locales/es.ts +466 -466
- package/src/locales/index.ts +19 -19
- package/src/locales/provider.tsx +59 -59
- package/src/locales/types.ts +77 -77
- package/src/order/index.ts +62 -62
- package/src/order/labels.ts +79 -79
- package/src/order/order-totals.tsx +250 -250
- package/src/primitives/select-field.tsx +93 -93
- package/src/products/image-gallery.tsx +43 -43
- package/src/products/index.ts +68 -59
- package/src/products/option-select.tsx +67 -67
- package/src/products/product-actions-wrapper.tsx +63 -62
- package/src/products/product-actions.tsx +151 -150
- package/src/products/product-price.tsx +69 -69
- package/src/products/product-promises.tsx +61 -61
- package/src/products/product-specs.tsx +65 -65
- package/src/products/product-tabs.tsx +123 -123
- package/src/products/product-template.tsx +163 -162
- package/src/products/purchase-options.tsx +130 -130
- package/src/products/sets.ts +110 -0
- package/src/products/use-product-actions.ts +199 -164
- package/src/products/variant-matching.ts +71 -71
- package/src/products/variant-url.ts +74 -74
- package/src/reviews-ui/index.ts +73 -73
- package/src/store/category-template.tsx +136 -136
- package/src/store/index.ts +40 -40
- package/src/tracking/chatgpt-pixel.tsx +99 -99
- package/src/tracking/consent-init.tsx +62 -62
- package/src/tracking/events.ts +348 -348
- package/src/tracking/ga4.tsx +93 -93
- package/src/tracking/google-ads.ts +84 -84
- package/src/tracking/gtm.tsx +60 -60
- package/src/tracking/inline-script.ts +49 -49
- package/src/tracking/oaiq.ts +206 -206
- package/src/tracking/tiktok-pixel.tsx +91 -91
- package/src/tracking/track-init.tsx +56 -56
- package/src/tracking/track-order-purchase.tsx +122 -122
- package/src/tracking/ttq.ts +180 -180
- package/src/tracking/use-tracking-config.ts +54 -54
- package/theme/index.css +25 -25
- package/theme/theme.css +150 -150
- package/theme/tokens.css +106 -106
package/src/api/search.ts
CHANGED
|
@@ -1,163 +1,163 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @cartbase/storefront/api/search — storefront search + related products
|
|
3
|
-
* (search-discovery card).
|
|
4
|
-
*
|
|
5
|
-
* Results are the SAME `StoreProduct` objects the products listing serves —
|
|
6
|
-
* reuse your product-card renderer as-is. Matching: full-text
|
|
7
|
-
* (Cyrillic-correct, prefix) ∪ title trigram typo tolerance ∪ SKU prefix ∪
|
|
8
|
-
* exact tag value; admin synonyms expand bidirectionally one level; admin
|
|
9
|
-
* pins for the exact query rank first, then global boosts, then relevance.
|
|
10
|
-
* Docs: docs/storefront/search.md.
|
|
11
|
-
*/
|
|
12
|
-
import type { Query, StorefrontClient } from "./http"
|
|
13
|
-
import type { PricingContextQuery } from "./types"
|
|
14
|
-
import type { StoreProduct } from "./products"
|
|
15
|
-
|
|
16
|
-
export interface SearchFacetBucket {
|
|
17
|
-
/** Filter value to send back (id, option value, or "min-max" for price). */
|
|
18
|
-
value: string
|
|
19
|
-
label: string
|
|
20
|
-
count: number
|
|
21
|
-
/** Range (price) buckets only — pass back as `price_min`/`price_max`. */
|
|
22
|
-
min?: number
|
|
23
|
-
max?: number | null
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface SearchFacet {
|
|
27
|
-
/** "price" | "availability" | "type" | "tags" | "collection" | "options.<Title>" */
|
|
28
|
-
key: string
|
|
29
|
-
label: string
|
|
30
|
-
type: "value" | "range"
|
|
31
|
-
buckets: SearchFacetBucket[]
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface SearchProductsQuery extends PricingContextQuery {
|
|
35
|
-
/** Required, min 1 char. */
|
|
36
|
-
q: string
|
|
37
|
-
collection_id?: string[]
|
|
38
|
-
category_id?: string[]
|
|
39
|
-
tag_id?: string[]
|
|
40
|
-
type_id?: string[]
|
|
41
|
-
/** Major units in the pricing currency, applied to the cheapest base price. */
|
|
42
|
-
price_min?: number
|
|
43
|
-
price_max?: number
|
|
44
|
-
availability?: "in_stock" | "out_of_stock"
|
|
45
|
-
/**
|
|
46
|
-
* Option filters: title → accepted values (OR within an option, AND
|
|
47
|
-
* across options). Serialized as `option.<Title>=v1,v2` params.
|
|
48
|
-
*/
|
|
49
|
-
options?: Record<string, string[]>
|
|
50
|
-
/** 1–100, default 20. */
|
|
51
|
-
limit?: number
|
|
52
|
-
offset?: number
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface SearchProductsResponse {
|
|
56
|
-
results: StoreProduct[]
|
|
57
|
-
/**
|
|
58
|
-
* Per the tenant's Settings → Search & discovery → Filters config.
|
|
59
|
-
* Counts follow the standard faceting rule: each facet is computed on the
|
|
60
|
-
* result set filtered by every OTHER active filter (own dimension
|
|
61
|
-
* excluded). Empty buckets and empty facets are omitted.
|
|
62
|
-
*/
|
|
63
|
-
facets: SearchFacet[]
|
|
64
|
-
total: number
|
|
65
|
-
offset: number
|
|
66
|
-
limit: number
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface RelatedProductsQuery extends PricingContextQuery {
|
|
70
|
-
/** 1–24, default 12. */
|
|
71
|
-
limit?: number
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Complementary products — the merchant's "goes with this" picks, for the
|
|
76
|
-
* slot ABOVE the product rather than the carousel below it.
|
|
77
|
-
*
|
|
78
|
-
* It has no fallback and never will: "similar to this" can be computed from
|
|
79
|
-
* collections and tags, "buy this with it" is a merchant judgement, and a
|
|
80
|
-
* guessed upsell beside the buy button is worse than an empty slot. An empty
|
|
81
|
-
* response means the merchant has not chosen — render nothing.
|
|
82
|
-
*/
|
|
83
|
-
export type ComplementaryProductsQuery = RelatedProductsQuery
|
|
84
|
-
|
|
85
|
-
export interface RelatedProductsResponse {
|
|
86
|
-
products: StoreProduct[]
|
|
87
|
-
count: number
|
|
88
|
-
/** True when any item came from the deterministic fallback chain. */
|
|
89
|
-
auto_filled: boolean
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** Fold the `options` record into `option.<Title>` wire params. */
|
|
93
|
-
function toSearchParams(query: SearchProductsQuery): Query {
|
|
94
|
-
const { options, ...rest } = query
|
|
95
|
-
const out: Query = { ...rest }
|
|
96
|
-
if (options) {
|
|
97
|
-
for (const [title, values] of Object.entries(options)) {
|
|
98
|
-
if (values.length) out[`option.${title}`] = values
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return out
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Search published products. Draft products, other tenants, and products
|
|
106
|
-
* outside the publishable key's channels NEVER appear (pins included).
|
|
107
|
-
*
|
|
108
|
-
* Auth: anon (`x-client-id`); optional `x-publishable-api-key` scopes the
|
|
109
|
-
* catalog to the key's channels (empty results when nothing matches);
|
|
110
|
-
* optional Bearer JWT makes `calculated_price` group-aware — never cache
|
|
111
|
-
* price-decorated responses shared.
|
|
112
|
-
*
|
|
113
|
-
* Errors: 400 `missing_client_id`, 400 `validation_failed` (missing/empty
|
|
114
|
-
* `q`, bad enum), 400 `invalid_publishable_key`, 400 `invalid_region`.
|
|
115
|
-
* Settings: Search & discovery — synonyms, pins/boosts, facet config
|
|
116
|
-
* (order/enabled, price bucket strategy).
|
|
117
|
-
*/
|
|
118
|
-
export async function searchProducts(
|
|
119
|
-
client: StorefrontClient,
|
|
120
|
-
query: SearchProductsQuery
|
|
121
|
-
): Promise<SearchProductsResponse> {
|
|
122
|
-
return client.get("/api/store/products/search", { query: toSearchParams(query) })
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Related products for a PDP: manual admin picks first (position order),
|
|
127
|
-
* then a deterministic fallback fills to `limit` — same primary collection
|
|
128
|
-
* (newest first), then most-shared-tags. Accepts a product id or handle.
|
|
129
|
-
*
|
|
130
|
-
* Auth: anon (`x-client-id`); optional publishable key — the anchor product
|
|
131
|
-
* must be visible to the key (404 otherwise) and scoped-away products never
|
|
132
|
-
* appear as related items. Errors: 404 `not_found` (unknown/draft/
|
|
133
|
-
* channel-hidden anchor), 400 `validation_failed`, 400 `invalid_region`.
|
|
134
|
-
* Settings: manual related picks (Admin → Product → Related).
|
|
135
|
-
*/
|
|
136
|
-
export async function listRelatedProducts(
|
|
137
|
-
client: StorefrontClient,
|
|
138
|
-
idOrHandle: string,
|
|
139
|
-
query?: RelatedProductsQuery
|
|
140
|
-
): Promise<RelatedProductsResponse> {
|
|
141
|
-
return client.get(`/api/store/products/${encodeURIComponent(idOrHandle)}/related`, {
|
|
142
|
-
query: { ...query },
|
|
143
|
-
})
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Complementary products for a PDP: the merchant's manual picks, in their
|
|
148
|
-
* order, and nothing else — `auto_filled` is always false.
|
|
149
|
-
*
|
|
150
|
-
* Same endpoint as related, discriminated by `kind`, because the two lists
|
|
151
|
-
* are one table and one code path apart only by meaning. Auth, errors and
|
|
152
|
-
* channel scoping are identical to `listRelatedProducts`.
|
|
153
|
-
* Settings: Admin → Product → Complementary products.
|
|
154
|
-
*/
|
|
155
|
-
export async function listComplementaryProducts(
|
|
156
|
-
client: StorefrontClient,
|
|
157
|
-
idOrHandle: string,
|
|
158
|
-
query?: ComplementaryProductsQuery
|
|
159
|
-
): Promise<RelatedProductsResponse> {
|
|
160
|
-
return client.get(`/api/store/products/${encodeURIComponent(idOrHandle)}/related`, {
|
|
161
|
-
query: { ...query, kind: "complementary" },
|
|
162
|
-
})
|
|
163
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @cartbase/storefront/api/search — storefront search + related products
|
|
3
|
+
* (search-discovery card).
|
|
4
|
+
*
|
|
5
|
+
* Results are the SAME `StoreProduct` objects the products listing serves —
|
|
6
|
+
* reuse your product-card renderer as-is. Matching: full-text
|
|
7
|
+
* (Cyrillic-correct, prefix) ∪ title trigram typo tolerance ∪ SKU prefix ∪
|
|
8
|
+
* exact tag value; admin synonyms expand bidirectionally one level; admin
|
|
9
|
+
* pins for the exact query rank first, then global boosts, then relevance.
|
|
10
|
+
* Docs: docs/storefront/search.md.
|
|
11
|
+
*/
|
|
12
|
+
import type { Query, StorefrontClient } from "./http"
|
|
13
|
+
import type { PricingContextQuery } from "./types"
|
|
14
|
+
import type { StoreProduct } from "./products"
|
|
15
|
+
|
|
16
|
+
export interface SearchFacetBucket {
|
|
17
|
+
/** Filter value to send back (id, option value, or "min-max" for price). */
|
|
18
|
+
value: string
|
|
19
|
+
label: string
|
|
20
|
+
count: number
|
|
21
|
+
/** Range (price) buckets only — pass back as `price_min`/`price_max`. */
|
|
22
|
+
min?: number
|
|
23
|
+
max?: number | null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface SearchFacet {
|
|
27
|
+
/** "price" | "availability" | "type" | "tags" | "collection" | "options.<Title>" */
|
|
28
|
+
key: string
|
|
29
|
+
label: string
|
|
30
|
+
type: "value" | "range"
|
|
31
|
+
buckets: SearchFacetBucket[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface SearchProductsQuery extends PricingContextQuery {
|
|
35
|
+
/** Required, min 1 char. */
|
|
36
|
+
q: string
|
|
37
|
+
collection_id?: string[]
|
|
38
|
+
category_id?: string[]
|
|
39
|
+
tag_id?: string[]
|
|
40
|
+
type_id?: string[]
|
|
41
|
+
/** Major units in the pricing currency, applied to the cheapest base price. */
|
|
42
|
+
price_min?: number
|
|
43
|
+
price_max?: number
|
|
44
|
+
availability?: "in_stock" | "out_of_stock"
|
|
45
|
+
/**
|
|
46
|
+
* Option filters: title → accepted values (OR within an option, AND
|
|
47
|
+
* across options). Serialized as `option.<Title>=v1,v2` params.
|
|
48
|
+
*/
|
|
49
|
+
options?: Record<string, string[]>
|
|
50
|
+
/** 1–100, default 20. */
|
|
51
|
+
limit?: number
|
|
52
|
+
offset?: number
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface SearchProductsResponse {
|
|
56
|
+
results: StoreProduct[]
|
|
57
|
+
/**
|
|
58
|
+
* Per the tenant's Settings → Search & discovery → Filters config.
|
|
59
|
+
* Counts follow the standard faceting rule: each facet is computed on the
|
|
60
|
+
* result set filtered by every OTHER active filter (own dimension
|
|
61
|
+
* excluded). Empty buckets and empty facets are omitted.
|
|
62
|
+
*/
|
|
63
|
+
facets: SearchFacet[]
|
|
64
|
+
total: number
|
|
65
|
+
offset: number
|
|
66
|
+
limit: number
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface RelatedProductsQuery extends PricingContextQuery {
|
|
70
|
+
/** 1–24, default 12. */
|
|
71
|
+
limit?: number
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Complementary products — the merchant's "goes with this" picks, for the
|
|
76
|
+
* slot ABOVE the product rather than the carousel below it.
|
|
77
|
+
*
|
|
78
|
+
* It has no fallback and never will: "similar to this" can be computed from
|
|
79
|
+
* collections and tags, "buy this with it" is a merchant judgement, and a
|
|
80
|
+
* guessed upsell beside the buy button is worse than an empty slot. An empty
|
|
81
|
+
* response means the merchant has not chosen — render nothing.
|
|
82
|
+
*/
|
|
83
|
+
export type ComplementaryProductsQuery = RelatedProductsQuery
|
|
84
|
+
|
|
85
|
+
export interface RelatedProductsResponse {
|
|
86
|
+
products: StoreProduct[]
|
|
87
|
+
count: number
|
|
88
|
+
/** True when any item came from the deterministic fallback chain. */
|
|
89
|
+
auto_filled: boolean
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Fold the `options` record into `option.<Title>` wire params. */
|
|
93
|
+
function toSearchParams(query: SearchProductsQuery): Query {
|
|
94
|
+
const { options, ...rest } = query
|
|
95
|
+
const out: Query = { ...rest }
|
|
96
|
+
if (options) {
|
|
97
|
+
for (const [title, values] of Object.entries(options)) {
|
|
98
|
+
if (values.length) out[`option.${title}`] = values
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return out
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Search published products. Draft products, other tenants, and products
|
|
106
|
+
* outside the publishable key's channels NEVER appear (pins included).
|
|
107
|
+
*
|
|
108
|
+
* Auth: anon (`x-client-id`); optional `x-publishable-api-key` scopes the
|
|
109
|
+
* catalog to the key's channels (empty results when nothing matches);
|
|
110
|
+
* optional Bearer JWT makes `calculated_price` group-aware — never cache
|
|
111
|
+
* price-decorated responses shared.
|
|
112
|
+
*
|
|
113
|
+
* Errors: 400 `missing_client_id`, 400 `validation_failed` (missing/empty
|
|
114
|
+
* `q`, bad enum), 400 `invalid_publishable_key`, 400 `invalid_region`.
|
|
115
|
+
* Settings: Search & discovery — synonyms, pins/boosts, facet config
|
|
116
|
+
* (order/enabled, price bucket strategy).
|
|
117
|
+
*/
|
|
118
|
+
export async function searchProducts(
|
|
119
|
+
client: StorefrontClient,
|
|
120
|
+
query: SearchProductsQuery
|
|
121
|
+
): Promise<SearchProductsResponse> {
|
|
122
|
+
return client.get("/api/store/products/search", { query: toSearchParams(query) })
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Related products for a PDP: manual admin picks first (position order),
|
|
127
|
+
* then a deterministic fallback fills to `limit` — same primary collection
|
|
128
|
+
* (newest first), then most-shared-tags. Accepts a product id or handle.
|
|
129
|
+
*
|
|
130
|
+
* Auth: anon (`x-client-id`); optional publishable key — the anchor product
|
|
131
|
+
* must be visible to the key (404 otherwise) and scoped-away products never
|
|
132
|
+
* appear as related items. Errors: 404 `not_found` (unknown/draft/
|
|
133
|
+
* channel-hidden anchor), 400 `validation_failed`, 400 `invalid_region`.
|
|
134
|
+
* Settings: manual related picks (Admin → Product → Related).
|
|
135
|
+
*/
|
|
136
|
+
export async function listRelatedProducts(
|
|
137
|
+
client: StorefrontClient,
|
|
138
|
+
idOrHandle: string,
|
|
139
|
+
query?: RelatedProductsQuery
|
|
140
|
+
): Promise<RelatedProductsResponse> {
|
|
141
|
+
return client.get(`/api/store/products/${encodeURIComponent(idOrHandle)}/related`, {
|
|
142
|
+
query: { ...query },
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Complementary products for a PDP: the merchant's manual picks, in their
|
|
148
|
+
* order, and nothing else — `auto_filled` is always false.
|
|
149
|
+
*
|
|
150
|
+
* Same endpoint as related, discriminated by `kind`, because the two lists
|
|
151
|
+
* are one table and one code path apart only by meaning. Auth, errors and
|
|
152
|
+
* channel scoping are identical to `listRelatedProducts`.
|
|
153
|
+
* Settings: Admin → Product → Complementary products.
|
|
154
|
+
*/
|
|
155
|
+
export async function listComplementaryProducts(
|
|
156
|
+
client: StorefrontClient,
|
|
157
|
+
idOrHandle: string,
|
|
158
|
+
query?: ComplementaryProductsQuery
|
|
159
|
+
): Promise<RelatedProductsResponse> {
|
|
160
|
+
return client.get(`/api/store/products/${encodeURIComponent(idOrHandle)}/related`, {
|
|
161
|
+
query: { ...query, kind: "complementary" },
|
|
162
|
+
})
|
|
163
|
+
}
|
package/src/api/store.ts
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @cartbase/storefront/api/store — the store's identity.
|
|
3
|
-
*
|
|
4
|
-
* Name, slug and brand (the merchant's Settings → Brand): what a
|
|
5
|
-
* storefront needs to title, head and foot itself without hardcoding a
|
|
6
|
-
* name. One anonymous read at layout level. Docs: docs/storefront/store.md.
|
|
7
|
-
*/
|
|
8
|
-
import type { StorefrontClient } from "./http"
|
|
9
|
-
|
|
10
|
-
export interface StoreBrand {
|
|
11
|
-
/** Primary logo, public URL, or null. */
|
|
12
|
-
logo_url: string | null
|
|
13
|
-
/** Square mark, public URL, or null. */
|
|
14
|
-
logo_square_url: string | null
|
|
15
|
-
/** `#rrggbb` or null. */
|
|
16
|
-
color_primary: string | null
|
|
17
|
-
/** `#rrggbb` or null. */
|
|
18
|
-
color_secondary: string | null
|
|
19
|
-
slogan: string | null
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface StoreIdentity {
|
|
23
|
-
/** The store's public id: what `<PlatformInit storeId>` mounts. */
|
|
24
|
-
id: string
|
|
25
|
-
/** The store's display name. */
|
|
26
|
-
name: string
|
|
27
|
-
/** The store's slug; its address is `{slug}.cartbase.net` until a custom domain. */
|
|
28
|
-
slug: string
|
|
29
|
-
brand: StoreBrand
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** GET /api/store/store — anon. */
|
|
33
|
-
export function getStore(client: StorefrontClient): Promise<{ store: StoreIdentity }> {
|
|
34
|
-
return client.request<{ store: StoreIdentity }>("/api/store/store")
|
|
35
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @cartbase/storefront/api/store — the store's identity.
|
|
3
|
+
*
|
|
4
|
+
* Name, slug and brand (the merchant's Settings → Brand): what a
|
|
5
|
+
* storefront needs to title, head and foot itself without hardcoding a
|
|
6
|
+
* name. One anonymous read at layout level. Docs: docs/storefront/store.md.
|
|
7
|
+
*/
|
|
8
|
+
import type { StorefrontClient } from "./http"
|
|
9
|
+
|
|
10
|
+
export interface StoreBrand {
|
|
11
|
+
/** Primary logo, public URL, or null. */
|
|
12
|
+
logo_url: string | null
|
|
13
|
+
/** Square mark, public URL, or null. */
|
|
14
|
+
logo_square_url: string | null
|
|
15
|
+
/** `#rrggbb` or null. */
|
|
16
|
+
color_primary: string | null
|
|
17
|
+
/** `#rrggbb` or null. */
|
|
18
|
+
color_secondary: string | null
|
|
19
|
+
slogan: string | null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface StoreIdentity {
|
|
23
|
+
/** The store's public id: what `<PlatformInit storeId>` mounts. */
|
|
24
|
+
id: string
|
|
25
|
+
/** The store's display name. */
|
|
26
|
+
name: string
|
|
27
|
+
/** The store's slug; its address is `{slug}.cartbase.net` until a custom domain. */
|
|
28
|
+
slug: string
|
|
29
|
+
brand: StoreBrand
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** GET /api/store/store — anon. */
|
|
33
|
+
export function getStore(client: StorefrontClient): Promise<{ store: StoreIdentity }> {
|
|
34
|
+
return client.request<{ store: StoreIdentity }>("/api/store/store")
|
|
35
|
+
}
|
package/src/api/types.ts
CHANGED
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @cartbase/storefront — shared API surface types.
|
|
3
|
-
*
|
|
4
|
-
* The envelope + error contract every domain module builds on. Shapes are
|
|
5
|
-
* the documented store-API contract (docs/contracts/store-api.md and the
|
|
6
|
-
* per-domain teaching docs under docs/storefront/): single entities come
|
|
7
|
-
* back as `{ <resource>: {...} }`, lists as `{ <resources>: [...], count,
|
|
8
|
-
* offset, limit }`, and errors as `{ error, code, details? }`.
|
|
9
|
-
*
|
|
10
|
-
* Domain modules own their DTOs; only cross-domain primitives live here.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/** List envelope every collection endpoint returns. */
|
|
14
|
-
export interface ListEnvelope {
|
|
15
|
-
count: number
|
|
16
|
-
offset: number
|
|
17
|
-
limit: number
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** The wire error body (`{ error, code, details? }`). */
|
|
21
|
-
export interface StoreApiErrorBody {
|
|
22
|
-
error?: string
|
|
23
|
-
message?: string
|
|
24
|
-
code?: string
|
|
25
|
-
details?: unknown
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Thrown for every non-2xx response. `status` + `code` are the stable
|
|
30
|
-
* programmatic surface (codes are contract-listed per endpoint, e.g.
|
|
31
|
-
* `invalid_gift_card`, `checkout_method_hidden`, `rate_limited`);
|
|
32
|
-
* `body` keeps the raw payload for anything else.
|
|
33
|
-
*/
|
|
34
|
-
export class StoreApiError extends Error {
|
|
35
|
-
readonly status: number
|
|
36
|
-
readonly code: string | null
|
|
37
|
-
readonly body: unknown
|
|
38
|
-
|
|
39
|
-
constructor(status: number, body: unknown) {
|
|
40
|
-
const parsed = (body ?? {}) as StoreApiErrorBody
|
|
41
|
-
super(parsed.error ?? parsed.message ?? `Store API error (${status})`)
|
|
42
|
-
this.name = "StoreApiError"
|
|
43
|
-
this.status = status
|
|
44
|
-
this.code = typeof parsed.code === "string" ? parsed.code : null
|
|
45
|
-
this.body = body
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** ISO-8601 timestamp string (the wire format for every date field). */
|
|
50
|
-
export type IsoDateString = string
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Money amounts are decimal major units (EUR 45.5, not cents) everywhere in
|
|
54
|
-
* the store API. Conversion to minor units happens only at provider
|
|
55
|
-
* boundaries server-side — the SDK never does money math (server truth).
|
|
56
|
-
*/
|
|
57
|
-
export type MajorUnitAmount = number
|
|
58
|
-
|
|
59
|
-
/** `variant.calculated_price` (b2b-v1 pricing context, store-api.md). */
|
|
60
|
-
export interface CalculatedPrice {
|
|
61
|
-
calculated_amount: MajorUnitAmount
|
|
62
|
-
original_amount: MajorUnitAmount
|
|
63
|
-
currency_code: string
|
|
64
|
-
is_calculated_price_price_list: boolean
|
|
65
|
-
price_list_id: string | null
|
|
66
|
-
price_list_type: "sale" | "override" | null
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** Raw base price row on variants (`product.prices[]` shape detail). */
|
|
70
|
-
export interface PriceRow {
|
|
71
|
-
amount: MajorUnitAmount
|
|
72
|
-
currency_code: string
|
|
73
|
-
min_quantity?: number | null
|
|
74
|
-
max_quantity?: number | null
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Pricing context accepted by every price-decorated listing (products,
|
|
79
|
-
* collections products, search, related). Server composes the rest
|
|
80
|
-
* (customer groups from the Bearer JWT, never from params).
|
|
81
|
-
*/
|
|
82
|
-
export interface PricingContextQuery {
|
|
83
|
-
currency_code?: string
|
|
84
|
-
region_id?: string
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Standard pagination query accepted by list endpoints. */
|
|
88
|
-
export interface PaginationQuery {
|
|
89
|
-
limit?: number
|
|
90
|
-
offset?: number
|
|
91
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @cartbase/storefront — shared API surface types.
|
|
3
|
+
*
|
|
4
|
+
* The envelope + error contract every domain module builds on. Shapes are
|
|
5
|
+
* the documented store-API contract (docs/contracts/store-api.md and the
|
|
6
|
+
* per-domain teaching docs under docs/storefront/): single entities come
|
|
7
|
+
* back as `{ <resource>: {...} }`, lists as `{ <resources>: [...], count,
|
|
8
|
+
* offset, limit }`, and errors as `{ error, code, details? }`.
|
|
9
|
+
*
|
|
10
|
+
* Domain modules own their DTOs; only cross-domain primitives live here.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** List envelope every collection endpoint returns. */
|
|
14
|
+
export interface ListEnvelope {
|
|
15
|
+
count: number
|
|
16
|
+
offset: number
|
|
17
|
+
limit: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The wire error body (`{ error, code, details? }`). */
|
|
21
|
+
export interface StoreApiErrorBody {
|
|
22
|
+
error?: string
|
|
23
|
+
message?: string
|
|
24
|
+
code?: string
|
|
25
|
+
details?: unknown
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Thrown for every non-2xx response. `status` + `code` are the stable
|
|
30
|
+
* programmatic surface (codes are contract-listed per endpoint, e.g.
|
|
31
|
+
* `invalid_gift_card`, `checkout_method_hidden`, `rate_limited`);
|
|
32
|
+
* `body` keeps the raw payload for anything else.
|
|
33
|
+
*/
|
|
34
|
+
export class StoreApiError extends Error {
|
|
35
|
+
readonly status: number
|
|
36
|
+
readonly code: string | null
|
|
37
|
+
readonly body: unknown
|
|
38
|
+
|
|
39
|
+
constructor(status: number, body: unknown) {
|
|
40
|
+
const parsed = (body ?? {}) as StoreApiErrorBody
|
|
41
|
+
super(parsed.error ?? parsed.message ?? `Store API error (${status})`)
|
|
42
|
+
this.name = "StoreApiError"
|
|
43
|
+
this.status = status
|
|
44
|
+
this.code = typeof parsed.code === "string" ? parsed.code : null
|
|
45
|
+
this.body = body
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** ISO-8601 timestamp string (the wire format for every date field). */
|
|
50
|
+
export type IsoDateString = string
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Money amounts are decimal major units (EUR 45.5, not cents) everywhere in
|
|
54
|
+
* the store API. Conversion to minor units happens only at provider
|
|
55
|
+
* boundaries server-side — the SDK never does money math (server truth).
|
|
56
|
+
*/
|
|
57
|
+
export type MajorUnitAmount = number
|
|
58
|
+
|
|
59
|
+
/** `variant.calculated_price` (b2b-v1 pricing context, store-api.md). */
|
|
60
|
+
export interface CalculatedPrice {
|
|
61
|
+
calculated_amount: MajorUnitAmount
|
|
62
|
+
original_amount: MajorUnitAmount
|
|
63
|
+
currency_code: string
|
|
64
|
+
is_calculated_price_price_list: boolean
|
|
65
|
+
price_list_id: string | null
|
|
66
|
+
price_list_type: "sale" | "override" | null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Raw base price row on variants (`product.prices[]` shape detail). */
|
|
70
|
+
export interface PriceRow {
|
|
71
|
+
amount: MajorUnitAmount
|
|
72
|
+
currency_code: string
|
|
73
|
+
min_quantity?: number | null
|
|
74
|
+
max_quantity?: number | null
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Pricing context accepted by every price-decorated listing (products,
|
|
79
|
+
* collections products, search, related). Server composes the rest
|
|
80
|
+
* (customer groups from the Bearer JWT, never from params).
|
|
81
|
+
*/
|
|
82
|
+
export interface PricingContextQuery {
|
|
83
|
+
currency_code?: string
|
|
84
|
+
region_id?: string
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Standard pagination query accepted by list endpoints. */
|
|
88
|
+
export interface PaginationQuery {
|
|
89
|
+
limit?: number
|
|
90
|
+
offset?: number
|
|
91
|
+
}
|