@shoppexio/storefront 1.0.57 → 1.0.59
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 +99 -0
- package/LICENSE +21 -0
- package/README.md +96 -180
- package/dist/chunk-ZVOFFURI.js +15364 -0
- package/dist/chunk-ZVOFFURI.js.map +1 -0
- package/dist/customer.cjs +16013 -1
- package/dist/customer.cjs.map +1 -1
- package/dist/{headless-customer.d.ts → customer.d.cts} +74 -61
- package/dist/customer.d.ts +336 -10
- package/dist/customer.js +726 -2
- package/dist/customer.js.map +1 -1
- package/dist/index.cjs +21533 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1979 -0
- package/dist/index.d.ts +1859 -49
- package/dist/index.js +6139 -0
- package/dist/index.js.map +1 -0
- package/package.json +18 -37
- package/dist/core/attribution.d.ts +0 -26
- package/dist/core/attribution.d.ts.map +0 -1
- package/dist/core/cache.d.ts +0 -26
- package/dist/core/cache.d.ts.map +0 -1
- package/dist/core/client.d.ts +0 -18
- package/dist/core/client.d.ts.map +0 -1
- package/dist/core/config.d.ts +0 -10
- package/dist/core/config.d.ts.map +0 -1
- package/dist/core/endpoint.d.ts +0 -2
- package/dist/core/endpoint.d.ts.map +0 -1
- package/dist/core/errors.d.ts +0 -34
- package/dist/core/errors.d.ts.map +0 -1
- package/dist/core/telemetry.d.ts +0 -20
- package/dist/core/telemetry.d.ts.map +0 -1
- package/dist/core/typed-client.d.ts +0 -15
- package/dist/core/typed-client.d.ts.map +0 -1
- package/dist/customer.d.ts.map +0 -1
- package/dist/headless-customer.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/modules/affiliates.d.ts +0 -15
- package/dist/modules/affiliates.d.ts.map +0 -1
- package/dist/modules/analytics.d.ts +0 -2
- package/dist/modules/analytics.d.ts.map +0 -1
- package/dist/modules/cart.d.ts +0 -62
- package/dist/modules/cart.d.ts.map +0 -1
- package/dist/modules/checkout.d.ts +0 -133
- package/dist/modules/checkout.d.ts.map +0 -1
- package/dist/modules/coupons.d.ts +0 -3
- package/dist/modules/coupons.d.ts.map +0 -1
- package/dist/modules/customer.d.ts +0 -147
- package/dist/modules/customer.d.ts.map +0 -1
- package/dist/modules/invoices.d.ts +0 -6
- package/dist/modules/invoices.d.ts.map +0 -1
- package/dist/modules/navigation.d.ts +0 -20
- package/dist/modules/navigation.d.ts.map +0 -1
- package/dist/modules/pages.d.ts +0 -10
- package/dist/modules/pages.d.ts.map +0 -1
- package/dist/modules/presence.d.ts +0 -12
- package/dist/modules/presence.d.ts.map +0 -1
- package/dist/modules/products.d.ts +0 -16
- package/dist/modules/products.d.ts.map +0 -1
- package/dist/modules/reviews.d.ts +0 -4
- package/dist/modules/reviews.d.ts.map +0 -1
- package/dist/modules/search.d.ts +0 -10
- package/dist/modules/search.d.ts.map +0 -1
- package/dist/modules/store.d.ts +0 -11
- package/dist/modules/store.d.ts.map +0 -1
- package/dist/modules/theme.d.ts +0 -27
- package/dist/modules/theme.d.ts.map +0 -1
- package/dist/shoppex.esm.js +0 -3
- package/dist/shoppex.esm.js.map +0 -1
- package/dist/shoppex.umd.js +0 -2
- package/dist/shoppex.umd.js.map +0 -1
- package/dist/types/api.d.ts +0 -757
- package/dist/types/api.d.ts.map +0 -1
- package/dist/types/cart.d.ts +0 -239
- package/dist/types/cart.d.ts.map +0 -1
- package/dist/types/config.d.ts +0 -18
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -7
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/theme-config.d.ts +0 -109
- package/dist/types/theme-config.d.ts.map +0 -1
- package/dist/utils/cart-line-id.d.ts +0 -5
- package/dist/utils/cart-line-id.d.ts.map +0 -1
- package/dist/utils/format.d.ts +0 -6
- package/dist/utils/format.d.ts.map +0 -1
- package/dist/utils/requested-currency.d.ts +0 -3
- package/dist/utils/requested-currency.d.ts.map +0 -1
- package/dist/utils/storage.d.ts +0 -10
- package/dist/utils/storage.d.ts.map +0 -1
- package/dist/utils/storefront-catalog.d.ts +0 -5
- package/dist/utils/storefront-catalog.d.ts.map +0 -1
- package/dist/utils/storefront-contact.d.ts +0 -32
- package/dist/utils/storefront-contact.d.ts.map +0 -1
- package/dist/utils/storefront-custom-fields.d.ts +0 -13
- package/dist/utils/storefront-custom-fields.d.ts.map +0 -1
- package/dist/utils/storefront-search.d.ts +0 -22
- package/dist/utils/storefront-search.d.ts.map +0 -1
- package/dist/utils/storefront-stock.d.ts +0 -14
- package/dist/utils/storefront-stock.d.ts.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,49 +1,1857 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
1
|
+
import { createApiClient, CustomerWarrantyClaimWire, CustomerPortalDashboardWire, CustomerPortalPaginatedInvoicesWire, CustomerPortalInvoiceDetailWire, CustomerLoyaltyWire, CustomerLoyaltyRedeemWire, CustomerWarrantyListWire } from '@shoppex/contracts';
|
|
2
|
+
import { NavigationMenuSlot } from '@shoppex/contracts/navigation';
|
|
3
|
+
export { NavigationMenuSlot } from '@shoppex/contracts/navigation';
|
|
4
|
+
import * as src from 'src';
|
|
5
|
+
export { CATALOG_UNIT_PRICE_DECIMAL_PLACES, CATALOG_UNIT_PRICE_FORMAT_OPTIONS, PAYABLE_AMOUNT_DECIMAL_PLACES, roundPayableAmount } from '@shoppex/contracts/catalog-unit-price';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* SDK Configuration Types
|
|
9
|
+
*/
|
|
10
|
+
interface ShoppexConfig {
|
|
11
|
+
storeSlug: string;
|
|
12
|
+
locale?: string;
|
|
13
|
+
currency?: string;
|
|
14
|
+
apiBaseUrl?: string;
|
|
15
|
+
checkoutBaseUrl?: string;
|
|
16
|
+
}
|
|
17
|
+
interface ShoppexInitOptions {
|
|
18
|
+
locale?: string;
|
|
19
|
+
currency?: string;
|
|
20
|
+
apiBaseUrl?: string;
|
|
21
|
+
checkoutBaseUrl?: string;
|
|
22
|
+
shopId?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* API Response Types
|
|
27
|
+
*
|
|
28
|
+
* Backend returns: { status: number, data: T, error: string | null }
|
|
29
|
+
* SDK maps to: { success: boolean, data?: T, message?: string }
|
|
30
|
+
*/
|
|
31
|
+
interface ApiResponse<T> {
|
|
32
|
+
status: number;
|
|
33
|
+
data: T;
|
|
34
|
+
error: string | null;
|
|
35
|
+
message?: string | null;
|
|
36
|
+
/**
|
|
37
|
+
* Machine-readable refusal identifier (e.g. `errors.checkout.coupon_no_longer_valid`).
|
|
38
|
+
* Emitted by every localized backend error; the localized `error` sentence is
|
|
39
|
+
* for humans, this is what code branches on.
|
|
40
|
+
*/
|
|
41
|
+
error_code?: string | null;
|
|
42
|
+
error_params?: Record<string, unknown> | null;
|
|
43
|
+
}
|
|
44
|
+
interface ApiChallenge {
|
|
45
|
+
provider: 'turnstile';
|
|
46
|
+
siteKey: string;
|
|
47
|
+
}
|
|
48
|
+
interface SDKResponse<T> {
|
|
49
|
+
success: boolean;
|
|
50
|
+
data?: T;
|
|
51
|
+
message?: string;
|
|
52
|
+
/**
|
|
53
|
+
* The backend's `error_code`, preserved verbatim on every failure the server
|
|
54
|
+
* answered — including non-2xx HTTP responses. `message` is localized display
|
|
55
|
+
* copy and must never be pattern-matched; branch on `code` instead.
|
|
56
|
+
* Absent when the failure never reached the server (transport, timeout) or
|
|
57
|
+
* when the endpoint emits no machine-readable code.
|
|
58
|
+
*/
|
|
59
|
+
code?: string;
|
|
60
|
+
errorParams?: Record<string, unknown>;
|
|
61
|
+
/**
|
|
62
|
+
* HTTP status of the server's answer, when there was one.
|
|
63
|
+
*
|
|
64
|
+
* Needed to tell "not signed in" (401) apart from "the server broke" (5xx) or
|
|
65
|
+
* a transport failure, which are otherwise indistinguishable. Without it a
|
|
66
|
+
* client has to treat every failure as no-session and show a sign-in form to
|
|
67
|
+
* someone who is signed in — a silent downgrade of the very state it is
|
|
68
|
+
* trying to report.
|
|
69
|
+
*
|
|
70
|
+
* Absent when the request never reached the server.
|
|
71
|
+
*/
|
|
72
|
+
status?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Whether the request received an HTTP response from the server.
|
|
75
|
+
*
|
|
76
|
+
* `false` identifies an ambiguous transport failure: the browser cannot
|
|
77
|
+
* know whether a mutation committed before the connection was lost. Public
|
|
78
|
+
* mutation helpers use this signal to retain their idempotency key for the
|
|
79
|
+
* buyer's retry. Absent on successful responses and older SDK results.
|
|
80
|
+
*/
|
|
81
|
+
responseReceived?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* True only when a parsed server refusal proves that the mutation did not
|
|
84
|
+
* commit (currently a JSON 4xx other than timeout). Mutation helpers may
|
|
85
|
+
* rotate an idempotency key on this signal. A response whose body is
|
|
86
|
+
* truncated, malformed, or a 5xx remains ambiguous even though headers were
|
|
87
|
+
* received.
|
|
88
|
+
*/
|
|
89
|
+
responseDefinitive?: boolean;
|
|
90
|
+
/** A renewable human-verification challenge returned with a refusal. */
|
|
91
|
+
challenge?: ApiChallenge;
|
|
92
|
+
}
|
|
93
|
+
interface Shop {
|
|
94
|
+
id: string;
|
|
95
|
+
name: string;
|
|
96
|
+
slug: string;
|
|
97
|
+
domain?: string;
|
|
98
|
+
description?: string;
|
|
99
|
+
title?: string;
|
|
100
|
+
message?: string;
|
|
101
|
+
url?: string;
|
|
102
|
+
logo?: string;
|
|
103
|
+
banner?: string;
|
|
104
|
+
avatar?: string;
|
|
105
|
+
average_score?: number | null;
|
|
106
|
+
rating?: number | null;
|
|
107
|
+
products_sold_count?: number;
|
|
108
|
+
customers_count?: number;
|
|
109
|
+
hide_products_sold?: boolean;
|
|
110
|
+
currency: string;
|
|
111
|
+
tos_enabled?: boolean;
|
|
112
|
+
dark_mode?: boolean;
|
|
113
|
+
search_enabled?: boolean;
|
|
114
|
+
sort_enabled?: boolean;
|
|
115
|
+
cart_enabled?: boolean;
|
|
116
|
+
contact_form_enabled?: boolean;
|
|
117
|
+
require_invoice_for_contact?: boolean;
|
|
118
|
+
hide_out_of_stock?: boolean;
|
|
119
|
+
hide_stock_counter?: boolean;
|
|
120
|
+
center_product_titles?: boolean;
|
|
121
|
+
center_group_titles?: boolean;
|
|
122
|
+
default_sort?: string;
|
|
123
|
+
social?: Record<string, string | null>;
|
|
124
|
+
discord_link?: string | null;
|
|
125
|
+
twitter_link?: string | null;
|
|
126
|
+
instagram_link?: string | null;
|
|
127
|
+
facebook_link?: string | null;
|
|
128
|
+
telegram_link?: string | null;
|
|
129
|
+
youtube_link?: string | null;
|
|
130
|
+
reddit_link?: string | null;
|
|
131
|
+
tiktok_link?: string | null;
|
|
132
|
+
verified?: boolean;
|
|
133
|
+
on_hold?: boolean;
|
|
134
|
+
theme?: ShopTheme | null;
|
|
135
|
+
builder_settings?: Record<string, unknown> | null;
|
|
136
|
+
feedback?: ShopFeedback;
|
|
137
|
+
trusted_checks?: TrustedChecks | null;
|
|
138
|
+
available_gateways?: string[];
|
|
139
|
+
payment_gateways_fees?: unknown[];
|
|
140
|
+
reward_program_summary?: RewardProgramSummary | null;
|
|
141
|
+
}
|
|
142
|
+
interface RewardProgramTeaserItem {
|
|
143
|
+
trigger_type: RewardReason;
|
|
144
|
+
action_type: RewardActionType;
|
|
145
|
+
label: string;
|
|
146
|
+
}
|
|
147
|
+
interface RewardProgramSummary {
|
|
148
|
+
enabled: boolean;
|
|
149
|
+
headline: string;
|
|
150
|
+
items: RewardProgramTeaserItem[];
|
|
151
|
+
}
|
|
152
|
+
interface ShopTheme {
|
|
153
|
+
primary_color?: string;
|
|
154
|
+
secondary_color?: string;
|
|
155
|
+
font?: string;
|
|
156
|
+
logo?: string | null;
|
|
157
|
+
logo_cloudflare_image_id?: string | null;
|
|
158
|
+
background_image?: string | null;
|
|
159
|
+
background_image_cloudflare_image_id?: string | null;
|
|
160
|
+
cards_in_row?: number | string;
|
|
161
|
+
cards_align?: string;
|
|
162
|
+
card_animation?: string;
|
|
163
|
+
dark_color?: string;
|
|
164
|
+
light_color?: string;
|
|
165
|
+
border_color?: string;
|
|
166
|
+
button_color?: string;
|
|
167
|
+
}
|
|
168
|
+
interface ShopFeedback {
|
|
169
|
+
positive: number;
|
|
170
|
+
neutral: number;
|
|
171
|
+
negative: number;
|
|
172
|
+
total: number;
|
|
173
|
+
}
|
|
174
|
+
interface TrustedChecks {
|
|
175
|
+
identity_verified?: boolean;
|
|
176
|
+
feedback_score?: string | null;
|
|
177
|
+
trusted_score?: number;
|
|
178
|
+
sales_last14days_metric?: string;
|
|
179
|
+
}
|
|
180
|
+
interface ProductCategory {
|
|
181
|
+
uniqid: string;
|
|
182
|
+
title: string;
|
|
183
|
+
description?: string | null;
|
|
184
|
+
image_attachment?: string | null;
|
|
185
|
+
image_name?: string | null;
|
|
186
|
+
image_storage?: string | null;
|
|
187
|
+
cloudflare_image_id?: string | null;
|
|
188
|
+
cdn_image_url?: string | null;
|
|
189
|
+
}
|
|
190
|
+
interface ProductDescriptionTab {
|
|
191
|
+
title: string;
|
|
192
|
+
content: string;
|
|
193
|
+
}
|
|
194
|
+
interface Product {
|
|
195
|
+
id?: string;
|
|
196
|
+
uniqid: string;
|
|
197
|
+
title: string;
|
|
198
|
+
slug?: string | null;
|
|
199
|
+
description?: string | null;
|
|
200
|
+
/** Merchant-defined description tabs; present when the shop configured them. */
|
|
201
|
+
description_tabs?: ProductDescriptionTab[];
|
|
202
|
+
product_highlights?: string[];
|
|
203
|
+
/**
|
|
204
|
+
* Merchant-defined public key/value pairs, set in the dashboard and readable
|
|
205
|
+
* here without a theme rebuild. Always present on storefront catalog reads —
|
|
206
|
+
* `{}` when the merchant set nothing — and values are always strings.
|
|
207
|
+
*
|
|
208
|
+
* Optional on this type because the older bound/legacy product payloads do not
|
|
209
|
+
* carry it; read it as `product.metadata?.status` on those paths.
|
|
210
|
+
*/
|
|
211
|
+
metadata?: Record<string, string>;
|
|
212
|
+
price: string;
|
|
213
|
+
price_display?: string;
|
|
214
|
+
price_discount?: number;
|
|
215
|
+
currency: string;
|
|
216
|
+
/**
|
|
217
|
+
* RAW local stock only. `-1` (or any negative value) means unlimited, `0` means
|
|
218
|
+
* no local units. This is NEVER on its own a purchasable cap: a supplier-backed
|
|
219
|
+
* product reports `stock: 0` by design and is still buyable. Gate buying on
|
|
220
|
+
* {@link Product.orderable}, and use `resolveDisplayStock` / `isProductInStock`
|
|
221
|
+
* rather than reading this field directly.
|
|
222
|
+
*/
|
|
223
|
+
stock?: number;
|
|
224
|
+
/**
|
|
225
|
+
* Per-product override for the shop's stock-counter setting; `null` inherits.
|
|
226
|
+
* Consumers must resolve it against the shop default rather than reading it
|
|
227
|
+
* alone.
|
|
228
|
+
*
|
|
229
|
+
* Both spellings are declared because both are really sent: the storefront
|
|
230
|
+
* catalog (`getStorefront`) emits camelCase alongside its other product
|
|
231
|
+
* fields, while `getProducts`, `getProduct` and `getStorefrontProductsPage`
|
|
232
|
+
* go through transformStorefrontCatalogProduct, which emits snake_case, and
|
|
233
|
+
* nothing normalizes between them. Declaring only one would have left readers
|
|
234
|
+
* of the other silently seeing `undefined` and falling back to the shop
|
|
235
|
+
* setting — showing a count the merchant hid, or hiding one they wanted shown.
|
|
236
|
+
*/
|
|
237
|
+
hideStockCounter?: boolean | null;
|
|
238
|
+
hide_stock_counter?: boolean | null;
|
|
239
|
+
/**
|
|
240
|
+
* Authoritative buyability predicate, mirrored from the backend's order-time
|
|
241
|
+
* gate: `local stock covers one unit OR an eligible supplier source can fulfil
|
|
242
|
+
* the line`. This — not `stock` — is what a storefront must gate the buy button,
|
|
243
|
+
* the quantity cap and the sold-out badge on.
|
|
244
|
+
*
|
|
245
|
+
* Absent on payloads that predate Dynamic Delivery. When absent, treat
|
|
246
|
+
* availability exactly as `stock` alone dictates; a missing field must never
|
|
247
|
+
* turn a genuinely sold-out product into a buyable one.
|
|
248
|
+
*/
|
|
249
|
+
orderable?: boolean;
|
|
250
|
+
/**
|
|
251
|
+
* True when an enabled supplier source fulfils this product instead of local
|
|
252
|
+
* stock. `orderable` remains the buy/sold-out gate; this field decides the
|
|
253
|
+
* QUANTITY CAP: the supplier purchase — not the local counter — fulfils the line
|
|
254
|
+
* and the completion excludes it from the local decrement, so residual local
|
|
255
|
+
* units bound nothing and the cap is unlimited even at `stock: 1`. Absent on
|
|
256
|
+
* payloads that predate Dynamic Delivery; when absent, the cap stays exactly
|
|
257
|
+
* what `stock` alone dictates.
|
|
258
|
+
*/
|
|
259
|
+
supplier_backed?: boolean;
|
|
260
|
+
quantity_min?: number;
|
|
261
|
+
quantity_max?: number;
|
|
262
|
+
min_quantity?: number;
|
|
263
|
+
max_quantity?: number;
|
|
264
|
+
on_hold?: boolean | number;
|
|
265
|
+
unlisted?: boolean | number;
|
|
266
|
+
private?: boolean | number;
|
|
267
|
+
bestseller?: boolean | number;
|
|
268
|
+
type?: string;
|
|
269
|
+
subtype?: string | null;
|
|
270
|
+
isSubscription?: boolean;
|
|
271
|
+
isGroup?: boolean;
|
|
272
|
+
images: ProductImage[];
|
|
273
|
+
image_name?: string | null;
|
|
274
|
+
image_storage?: string | null;
|
|
275
|
+
cloudflare_image_id?: string | null;
|
|
276
|
+
image_attachment?: string | null;
|
|
277
|
+
sold_count?: number;
|
|
278
|
+
soldCount?: number;
|
|
279
|
+
variants?: ProductVariant[];
|
|
280
|
+
addons?: ProductAddon[];
|
|
281
|
+
price_variants?: PriceVariant[];
|
|
282
|
+
volume_discounts?: unknown;
|
|
283
|
+
custom_fields?: string | unknown[] | null;
|
|
284
|
+
categories?: ProductCategory[];
|
|
285
|
+
bundles?: ProductBundle[];
|
|
286
|
+
faqs?: ProductFaq[];
|
|
287
|
+
subscription?: Subscription;
|
|
288
|
+
feedbacks?: Feedback[];
|
|
289
|
+
feedback?: ProductFeedback;
|
|
290
|
+
cdn_image_url?: string;
|
|
291
|
+
detail_image_url?: string | null;
|
|
292
|
+
recurring_interval?: string | null;
|
|
293
|
+
recurring_interval_count?: number | null;
|
|
294
|
+
trial_period?: number | null;
|
|
295
|
+
setup_cost?: string;
|
|
296
|
+
licensing_enabled?: boolean | number;
|
|
297
|
+
license_period?: string | number | null;
|
|
298
|
+
delivery_text?: string;
|
|
299
|
+
service_text?: string;
|
|
300
|
+
terms_of_service?: string | null;
|
|
301
|
+
warranty?: number;
|
|
302
|
+
warranty_text?: string | null;
|
|
303
|
+
sort_priority?: number;
|
|
304
|
+
pay_what_you_want?: boolean | number;
|
|
305
|
+
video_link?: string | null;
|
|
306
|
+
}
|
|
307
|
+
interface ProductFeedback {
|
|
308
|
+
total: number;
|
|
309
|
+
positive: number;
|
|
310
|
+
neutral: number;
|
|
311
|
+
negative: number;
|
|
312
|
+
numbers?: Record<number, number>;
|
|
313
|
+
list?: Array<{
|
|
314
|
+
score: number;
|
|
315
|
+
message: string | null;
|
|
316
|
+
reply: string | null;
|
|
317
|
+
created_at: string | null;
|
|
318
|
+
createdAt?: string | null;
|
|
319
|
+
updated_at: string | null;
|
|
320
|
+
updatedAt?: string | null;
|
|
321
|
+
is_automated?: boolean;
|
|
322
|
+
isAutomated?: boolean;
|
|
323
|
+
}>;
|
|
324
|
+
}
|
|
325
|
+
interface ProductImage {
|
|
326
|
+
id: string;
|
|
327
|
+
url: string;
|
|
328
|
+
cloudflare_image_id?: string;
|
|
329
|
+
alt?: string;
|
|
330
|
+
}
|
|
331
|
+
interface ProductVariant {
|
|
332
|
+
id: string;
|
|
333
|
+
title: string;
|
|
334
|
+
price?: number;
|
|
335
|
+
/**
|
|
336
|
+
* RAW local stock for this option only. `-1` (or any negative value) means
|
|
337
|
+
* unlimited, `0` means no local units — which a supplier-backed option reports
|
|
338
|
+
* by design. Never render this as a purchasable cap on its own; gate on
|
|
339
|
+
* {@link ProductVariant.orderable}.
|
|
340
|
+
*/
|
|
341
|
+
stock?: number;
|
|
342
|
+
/**
|
|
343
|
+
* Authoritative buyability predicate for this option: `local stock covers one
|
|
344
|
+
* unit OR an eligible supplier source can fulfil the line`. The backend resolves
|
|
345
|
+
* a product-wide supplier mapping onto every variant's own flag, so this alone
|
|
346
|
+
* is authoritative for a selected variant.
|
|
347
|
+
*
|
|
348
|
+
* Absent on payloads that predate Dynamic Delivery; when absent, availability
|
|
349
|
+
* follows `stock` alone, exactly as before.
|
|
350
|
+
*/
|
|
351
|
+
orderable?: boolean;
|
|
352
|
+
/**
|
|
353
|
+
* True when an enabled supplier source fulfils this option instead of local
|
|
354
|
+
* stock. `orderable` remains the buy/sold-out gate; this field decides the
|
|
355
|
+
* QUANTITY CAP — a supplier-backed row is unbounded even when it still holds a
|
|
356
|
+
* few local units, because the supplier purchase fulfils the line and the
|
|
357
|
+
* completion excludes it from the local decrement. Absent on payloads that
|
|
358
|
+
* predate Dynamic Delivery.
|
|
359
|
+
*/
|
|
360
|
+
supplier_backed?: boolean;
|
|
361
|
+
quantity_min?: number;
|
|
362
|
+
quantity_max?: number;
|
|
363
|
+
quantityMin?: number;
|
|
364
|
+
quantityMax?: number;
|
|
365
|
+
image_id?: string | null;
|
|
366
|
+
imageId?: string | null;
|
|
367
|
+
cloudflare_image_id?: string | null;
|
|
368
|
+
cloudflareImageId?: string | null;
|
|
369
|
+
image_url?: string | null;
|
|
370
|
+
imageUrl?: string | null;
|
|
371
|
+
/** Merchant-selected storefront highlight. It does not affect selection or availability. */
|
|
372
|
+
is_featured?: boolean;
|
|
373
|
+
isFeatured?: boolean;
|
|
374
|
+
/** License validity in days for this option. Zero means lifetime. */
|
|
375
|
+
license_period?: number | null;
|
|
376
|
+
}
|
|
377
|
+
interface ProductAddon {
|
|
378
|
+
id: string;
|
|
379
|
+
name?: string;
|
|
380
|
+
title?: string;
|
|
381
|
+
price: number;
|
|
382
|
+
required?: boolean;
|
|
383
|
+
}
|
|
384
|
+
interface PriceVariant {
|
|
385
|
+
id: string;
|
|
386
|
+
label?: string;
|
|
387
|
+
title?: string;
|
|
388
|
+
price: number;
|
|
389
|
+
/**
|
|
390
|
+
* RAW local stock for this price variant only. `-1` (or any negative value)
|
|
391
|
+
* means unlimited, `0` means no local units — which a supplier-backed variant
|
|
392
|
+
* reports by design. Never render this as a purchasable cap on its own; gate on
|
|
393
|
+
* {@link PriceVariant.orderable}.
|
|
394
|
+
*/
|
|
395
|
+
stock?: number;
|
|
396
|
+
/**
|
|
397
|
+
* Authoritative buyability predicate for this price variant: `local stock covers
|
|
398
|
+
* one unit OR an eligible supplier source can fulfil the line`. The backend
|
|
399
|
+
* resolves a product-wide supplier mapping onto every variant's own flag, so
|
|
400
|
+
* this alone is authoritative for a selected variant.
|
|
401
|
+
*
|
|
402
|
+
* Absent on payloads that predate Dynamic Delivery; when absent, availability
|
|
403
|
+
* follows `stock` alone, exactly as before.
|
|
404
|
+
*/
|
|
405
|
+
orderable?: boolean;
|
|
406
|
+
/**
|
|
407
|
+
* True when an enabled supplier source fulfils this price variant instead of
|
|
408
|
+
* local stock. `orderable` remains the buy/sold-out gate; this field decides the
|
|
409
|
+
* QUANTITY CAP — a supplier-backed row is unbounded even when it still holds a
|
|
410
|
+
* few local units, because the supplier purchase fulfils the line and the
|
|
411
|
+
* completion excludes it from the local decrement. Absent on payloads that
|
|
412
|
+
* predate Dynamic Delivery.
|
|
413
|
+
*/
|
|
414
|
+
supplier_backed?: boolean;
|
|
415
|
+
quantity_min?: number;
|
|
416
|
+
quantity_max?: number;
|
|
417
|
+
quantityMin?: number;
|
|
418
|
+
quantityMax?: number;
|
|
419
|
+
image_id?: string | null;
|
|
420
|
+
imageId?: string | null;
|
|
421
|
+
cloudflare_image_id?: string | null;
|
|
422
|
+
cloudflareImageId?: string | null;
|
|
423
|
+
image_url?: string | null;
|
|
424
|
+
imageUrl?: string | null;
|
|
425
|
+
/** Merchant-selected storefront highlight. It does not affect selection or availability. */
|
|
426
|
+
is_featured?: boolean;
|
|
427
|
+
isFeatured?: boolean;
|
|
428
|
+
/** License validity in days for this option. Zero means lifetime. */
|
|
429
|
+
license_period?: number | null;
|
|
430
|
+
}
|
|
431
|
+
interface CustomFieldDefinition {
|
|
432
|
+
id: string;
|
|
433
|
+
name: string;
|
|
434
|
+
type: 'text' | 'textarea' | 'select' | 'checkbox';
|
|
435
|
+
required?: boolean;
|
|
436
|
+
options?: string[];
|
|
437
|
+
}
|
|
438
|
+
interface Feedback {
|
|
439
|
+
id: string;
|
|
440
|
+
rating: number;
|
|
441
|
+
score?: number;
|
|
442
|
+
author?: string;
|
|
443
|
+
customer_name?: string;
|
|
444
|
+
comment?: string;
|
|
445
|
+
message?: string | null;
|
|
446
|
+
reply?: string | null;
|
|
447
|
+
is_automated?: boolean;
|
|
448
|
+
isAutomated?: boolean;
|
|
449
|
+
product_id?: string | null;
|
|
450
|
+
product_title?: string | null;
|
|
451
|
+
cloudflare_image_id?: string | null;
|
|
452
|
+
created_at: string;
|
|
453
|
+
updated_at?: string | number | null;
|
|
454
|
+
}
|
|
455
|
+
interface ShopReviewsSummary {
|
|
456
|
+
total: number;
|
|
457
|
+
positive: number;
|
|
458
|
+
neutral: number;
|
|
459
|
+
negative: number;
|
|
460
|
+
average_score: number | null;
|
|
461
|
+
}
|
|
462
|
+
interface ShopReviewsPage {
|
|
463
|
+
feedback: Feedback[];
|
|
464
|
+
pagination: CursorPagination;
|
|
465
|
+
summary: ShopReviewsSummary;
|
|
466
|
+
}
|
|
467
|
+
interface ProductBundle {
|
|
468
|
+
id?: string;
|
|
469
|
+
uniqid?: string;
|
|
470
|
+
title?: string;
|
|
471
|
+
discount_type?: 'PERCENTAGE' | 'FIXED';
|
|
472
|
+
discount_amount?: number;
|
|
473
|
+
products?: ProductBundleProduct[];
|
|
474
|
+
}
|
|
475
|
+
interface ProductBundleProduct {
|
|
476
|
+
id?: string;
|
|
477
|
+
uniqid: string;
|
|
478
|
+
title?: string | null;
|
|
479
|
+
default_variant_id?: string;
|
|
480
|
+
}
|
|
481
|
+
interface ProductFaq {
|
|
482
|
+
id?: string;
|
|
483
|
+
uniqid?: string;
|
|
484
|
+
question: string;
|
|
485
|
+
answer: string;
|
|
486
|
+
}
|
|
487
|
+
interface SubscriptionInterval {
|
|
488
|
+
unit?: string;
|
|
489
|
+
count?: number;
|
|
490
|
+
label?: string;
|
|
491
|
+
}
|
|
492
|
+
interface SubscriptionFlags {
|
|
493
|
+
cancel_anytime?: boolean;
|
|
494
|
+
}
|
|
495
|
+
interface Subscription {
|
|
496
|
+
id?: string;
|
|
497
|
+
name?: string;
|
|
498
|
+
description?: string;
|
|
499
|
+
badge?: string;
|
|
500
|
+
interval?: SubscriptionInterval;
|
|
501
|
+
flags?: SubscriptionFlags;
|
|
502
|
+
}
|
|
503
|
+
interface Invoice {
|
|
504
|
+
uniqid: string;
|
|
505
|
+
status: string;
|
|
506
|
+
total: number;
|
|
507
|
+
currency: string;
|
|
508
|
+
gateway?: string;
|
|
509
|
+
products: InvoiceProduct[];
|
|
510
|
+
created_at: string;
|
|
511
|
+
rewards?: BuyerRewards | null;
|
|
512
|
+
}
|
|
513
|
+
interface InvoiceProduct {
|
|
514
|
+
product_id: string;
|
|
515
|
+
title: string;
|
|
516
|
+
quantity: number;
|
|
517
|
+
price: number;
|
|
518
|
+
}
|
|
519
|
+
type RewardActionType = 'WALLET_CREDIT' | 'COUPON';
|
|
520
|
+
type RewardGrantStatus = 'PENDING' | 'FULFILLED' | 'FAILED' | 'REVOKED';
|
|
521
|
+
type RewardReason = 'ORDER_COMPLETED' | 'ORDER_COUNT_REACHED' | 'SPEND_AMOUNT_REACHED' | 'POSITIVE_REVIEW_LEFT' | 'FIRST_PURCHASE_COMPLETED';
|
|
522
|
+
interface BuyerRewardActivityItem {
|
|
523
|
+
id: string;
|
|
524
|
+
type: RewardActionType;
|
|
525
|
+
status: RewardGrantStatus;
|
|
526
|
+
amount: string | null;
|
|
527
|
+
currency: string | null;
|
|
528
|
+
coupon_code: string | null;
|
|
529
|
+
reason: RewardReason;
|
|
530
|
+
trigger_reference_type: string;
|
|
531
|
+
trigger_reference_id: string;
|
|
532
|
+
created_at: number;
|
|
533
|
+
fulfilled_at: number | null;
|
|
534
|
+
expires_at: number | null;
|
|
535
|
+
}
|
|
536
|
+
interface BuyerRewards {
|
|
537
|
+
summary: {
|
|
538
|
+
available: string;
|
|
539
|
+
pending: string;
|
|
540
|
+
lifetime_earned: string;
|
|
541
|
+
redeemed: string | null;
|
|
542
|
+
currency: string;
|
|
543
|
+
};
|
|
544
|
+
activity: BuyerRewardActivityItem[];
|
|
545
|
+
earned_after_invoice: BuyerRewardActivityItem[];
|
|
546
|
+
pending_after_invoice: BuyerRewardActivityItem[];
|
|
547
|
+
}
|
|
548
|
+
interface CouponValidation {
|
|
549
|
+
valid: boolean;
|
|
550
|
+
discount?: number;
|
|
551
|
+
discount_type?: 'percentage' | 'fixed';
|
|
552
|
+
first_order_only?: boolean;
|
|
553
|
+
source?: 'COUPON' | 'AFFILIATE';
|
|
554
|
+
product_restricted?: boolean;
|
|
555
|
+
variant_restricted?: boolean;
|
|
556
|
+
restriction_scope?: 'all' | 'products' | 'variants' | 'products_and_variants';
|
|
557
|
+
allowed_product_ids?: string[];
|
|
558
|
+
allowed_variant_ids?: string[];
|
|
559
|
+
message?: string;
|
|
560
|
+
}
|
|
561
|
+
interface CouponValidationOptions {
|
|
562
|
+
productId?: string;
|
|
563
|
+
variantId?: string;
|
|
564
|
+
}
|
|
565
|
+
interface AffiliateValidation {
|
|
566
|
+
valid: boolean;
|
|
567
|
+
program_enabled?: boolean;
|
|
568
|
+
affiliate_code: string | null;
|
|
569
|
+
discount_active: boolean;
|
|
570
|
+
discount_percent: number;
|
|
571
|
+
message?: string;
|
|
572
|
+
}
|
|
573
|
+
interface ProductGroup {
|
|
574
|
+
id?: string;
|
|
575
|
+
uniqid: string;
|
|
576
|
+
category_id?: string | null;
|
|
577
|
+
slug?: string | null;
|
|
578
|
+
name?: string | null;
|
|
579
|
+
title: string;
|
|
580
|
+
description?: string | null;
|
|
581
|
+
product_uniqids?: string[];
|
|
582
|
+
products_count?: number;
|
|
583
|
+
image_attachment?: string | null;
|
|
584
|
+
image_name?: string | null;
|
|
585
|
+
image_storage?: string | null;
|
|
586
|
+
cloudflare_image_id?: string | null;
|
|
587
|
+
cdn_image_url?: string | null;
|
|
588
|
+
sort_priority?: number | null;
|
|
589
|
+
}
|
|
590
|
+
interface Category {
|
|
591
|
+
uniqid: string;
|
|
592
|
+
title: string;
|
|
593
|
+
description?: string | null;
|
|
594
|
+
image_attachment?: string | null;
|
|
595
|
+
image_name?: string | null;
|
|
596
|
+
image_storage?: string | null;
|
|
597
|
+
cloudflare_image_id?: string | null;
|
|
598
|
+
cdn_image_url?: string | null;
|
|
599
|
+
product_uniqids?: string[];
|
|
600
|
+
products_bound?: Product[];
|
|
601
|
+
products_count?: number;
|
|
602
|
+
groups_bound?: ProductGroup[];
|
|
603
|
+
groups_count?: number;
|
|
604
|
+
}
|
|
605
|
+
interface CursorPagination {
|
|
606
|
+
next_cursor: string | null;
|
|
607
|
+
has_more: boolean;
|
|
608
|
+
}
|
|
609
|
+
interface StorefrontAnnouncementBarAddon {
|
|
610
|
+
id: string;
|
|
611
|
+
type: 'announcement_bar';
|
|
612
|
+
slot: string;
|
|
613
|
+
component: 'announcement_bar';
|
|
614
|
+
sort_order: number;
|
|
615
|
+
props: {
|
|
616
|
+
addon_id: string;
|
|
617
|
+
text: string;
|
|
618
|
+
link_label?: string | null;
|
|
619
|
+
link_url?: string | null;
|
|
620
|
+
dismissible: boolean;
|
|
621
|
+
display_mode: 'static' | 'marquee';
|
|
622
|
+
theme_preset: 'brand_blue' | 'emerald' | 'sunset' | 'rose' | 'charcoal' | 'custom';
|
|
623
|
+
animation_speed_seconds: number;
|
|
624
|
+
background_color: string;
|
|
625
|
+
text_color: string;
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
interface StorefrontCountdownBarAddon {
|
|
629
|
+
id: string;
|
|
630
|
+
type: 'countdown_bar';
|
|
631
|
+
slot: string;
|
|
632
|
+
component: 'countdown_bar';
|
|
633
|
+
sort_order: number;
|
|
634
|
+
props: {
|
|
635
|
+
addon_id: string;
|
|
636
|
+
text: string;
|
|
637
|
+
end_at: string;
|
|
638
|
+
link_label?: string | null;
|
|
639
|
+
link_url?: string | null;
|
|
640
|
+
dismissible: boolean;
|
|
641
|
+
theme_preset: 'brand_blue' | 'emerald' | 'sunset' | 'rose' | 'charcoal' | 'custom';
|
|
642
|
+
expired_behavior: 'hide' | 'message';
|
|
643
|
+
expired_message?: string | null;
|
|
644
|
+
density: 'compact' | 'comfortable';
|
|
645
|
+
cta_style: 'subtle' | 'outline' | 'solid';
|
|
646
|
+
timer_style: 'minimal' | 'boxed';
|
|
647
|
+
background_color: string;
|
|
648
|
+
text_color: string;
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
interface StorefrontPromoInfoCardAddon {
|
|
652
|
+
id: string;
|
|
653
|
+
type: 'promo_info_card';
|
|
654
|
+
slot: string;
|
|
655
|
+
component: 'promo_info_card';
|
|
656
|
+
sort_order: number;
|
|
657
|
+
props: {
|
|
658
|
+
addon_id: string;
|
|
659
|
+
eyebrow?: string | null;
|
|
660
|
+
title: string;
|
|
661
|
+
body: string;
|
|
662
|
+
link_label?: string | null;
|
|
663
|
+
link_url?: string | null;
|
|
664
|
+
theme_preset: 'indigo' | 'emerald' | 'amber' | 'rose' | 'slate' | 'custom';
|
|
665
|
+
layout_style: 'compact' | 'feature' | 'alert';
|
|
666
|
+
density: 'compact' | 'comfortable';
|
|
667
|
+
cta_style: 'subtle' | 'outline' | 'solid';
|
|
668
|
+
icon: 'sparkles' | 'megaphone' | 'gift' | 'truck' | 'shield' | 'support' | 'none';
|
|
669
|
+
icon_visibility: 'show' | 'hide';
|
|
670
|
+
background_color: string;
|
|
671
|
+
text_color: string;
|
|
672
|
+
accent_color: string;
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
interface StorefrontRecentPurchasePopupAddon {
|
|
676
|
+
id: string;
|
|
677
|
+
type: 'recent_purchase_popup';
|
|
678
|
+
slot: string;
|
|
679
|
+
component: 'recent_purchase_popup';
|
|
680
|
+
sort_order: number;
|
|
681
|
+
props: {
|
|
682
|
+
addon_id: string;
|
|
683
|
+
title: string;
|
|
684
|
+
cooldown_seconds: number;
|
|
685
|
+
items: Array<{
|
|
686
|
+
customer_label: string;
|
|
687
|
+
product_title: string;
|
|
688
|
+
created_at: string;
|
|
689
|
+
}>;
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
interface StorefrontCouponPopupModalAddon {
|
|
693
|
+
id: string;
|
|
694
|
+
type: 'coupon_popup_modal';
|
|
695
|
+
slot: string;
|
|
696
|
+
component: 'coupon_popup_modal';
|
|
697
|
+
sort_order: number;
|
|
698
|
+
props: {
|
|
699
|
+
addon_id: string;
|
|
700
|
+
eyebrow?: string | null;
|
|
701
|
+
title: string;
|
|
702
|
+
body: string;
|
|
703
|
+
coupon_code: string;
|
|
704
|
+
primary_button_label: string;
|
|
705
|
+
secondary_button_label: string;
|
|
706
|
+
disclaimer?: string | null;
|
|
707
|
+
theme_preset: 'midnight' | 'ocean' | 'ember' | 'forest' | 'custom';
|
|
708
|
+
trigger: 'delay' | 'exit_intent';
|
|
709
|
+
delay_seconds: number;
|
|
710
|
+
show_once_per_session: boolean;
|
|
711
|
+
reminder_hours: number;
|
|
712
|
+
background_color: string;
|
|
713
|
+
text_color: string;
|
|
714
|
+
accent_color: string;
|
|
715
|
+
hero_image_url?: string | null;
|
|
716
|
+
discount_display?: string | null;
|
|
717
|
+
expires_at?: string | null;
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
type StorefrontAddon = StorefrontAnnouncementBarAddon | StorefrontCountdownBarAddon | StorefrontPromoInfoCardAddon | StorefrontRecentPurchasePopupAddon | StorefrontCouponPopupModalAddon;
|
|
721
|
+
interface StorefrontAddonBootstrap {
|
|
722
|
+
items: StorefrontAddon[];
|
|
723
|
+
}
|
|
724
|
+
interface StorefrontOnlineUsers {
|
|
725
|
+
count: number;
|
|
726
|
+
window_seconds: number;
|
|
727
|
+
}
|
|
728
|
+
interface StorefrontRecentSaleEntry {
|
|
729
|
+
product_title: string;
|
|
730
|
+
seconds_ago: number;
|
|
731
|
+
}
|
|
732
|
+
interface StorefrontRecentSales {
|
|
733
|
+
items: StorefrontRecentSaleEntry[];
|
|
734
|
+
window_hours: number;
|
|
735
|
+
}
|
|
736
|
+
type StorefrontItem = {
|
|
737
|
+
item_type: 'PRODUCT' | 'GROUP';
|
|
738
|
+
uniqid: string;
|
|
739
|
+
sort_priority?: number | null;
|
|
740
|
+
title?: string | null;
|
|
741
|
+
};
|
|
742
|
+
interface StorefrontData {
|
|
743
|
+
shop: Shop;
|
|
744
|
+
products: Product[];
|
|
745
|
+
products_pagination?: CursorPagination | null;
|
|
746
|
+
groups: ProductGroup[];
|
|
747
|
+
items: StorefrontItem[];
|
|
748
|
+
categories: Category[];
|
|
749
|
+
addons: StorefrontAddonBootstrap;
|
|
750
|
+
}
|
|
751
|
+
interface Page {
|
|
752
|
+
id: string;
|
|
753
|
+
shop_id?: string;
|
|
754
|
+
slug: string;
|
|
755
|
+
name: string;
|
|
756
|
+
content: string | null;
|
|
757
|
+
template_type: string;
|
|
758
|
+
is_system: boolean;
|
|
759
|
+
settings: Record<string, unknown> | null;
|
|
760
|
+
visible_after: string | null;
|
|
761
|
+
created_at: number;
|
|
762
|
+
updated_at: number;
|
|
763
|
+
}
|
|
764
|
+
interface Menu {
|
|
765
|
+
id: string;
|
|
766
|
+
shop_id: string;
|
|
767
|
+
title: string;
|
|
768
|
+
slot?: NavigationMenuSlot | null;
|
|
769
|
+
item_count?: number | null;
|
|
770
|
+
items?: MenuItem[];
|
|
771
|
+
created_at: number;
|
|
772
|
+
updated_at: number;
|
|
773
|
+
}
|
|
774
|
+
interface MenuItem {
|
|
775
|
+
id: string;
|
|
776
|
+
shop_id?: string;
|
|
777
|
+
menu_id: string;
|
|
778
|
+
page_id: string | null;
|
|
779
|
+
title: string;
|
|
780
|
+
url?: string | null;
|
|
781
|
+
link_type?: 'LINK' | 'PAGE' | 'link' | 'page' | null;
|
|
782
|
+
target?: '_self' | '_blank' | null;
|
|
783
|
+
sort_order: number;
|
|
784
|
+
page_slug: string | null;
|
|
785
|
+
created_at: number;
|
|
786
|
+
updated_at: number;
|
|
787
|
+
children?: MenuItem[];
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* Cart Types
|
|
792
|
+
*
|
|
793
|
+
* Extended cart schema supporting Shoppex features:
|
|
794
|
+
* - Addons (express shipping, gift wrap, etc.)
|
|
795
|
+
* - Custom Fields (engraving, gift message, etc.)
|
|
796
|
+
* - Price Variants (different pricing tiers)
|
|
797
|
+
*
|
|
798
|
+
* All fields use snake_case to match API conventions.
|
|
799
|
+
*/
|
|
800
|
+
interface CartAddon {
|
|
801
|
+
id: string;
|
|
802
|
+
quantity?: number;
|
|
803
|
+
}
|
|
804
|
+
/** Incoming basket rows may omit line_id; mergeBaskets derives it before deduping. */
|
|
805
|
+
type CartBasketMergeLine = Omit<CartItem, 'line_id'> & {
|
|
806
|
+
line_id?: string;
|
|
807
|
+
};
|
|
808
|
+
interface CartItem {
|
|
809
|
+
/** Stable identity for this configuration (product + variant + addons + custom_fields + price snapshot). */
|
|
810
|
+
line_id: string;
|
|
811
|
+
product_id: string;
|
|
812
|
+
variant_id: string;
|
|
813
|
+
quantity: number;
|
|
814
|
+
addons?: CartAddon[];
|
|
815
|
+
custom_fields?: Record<string, string>;
|
|
816
|
+
price_variant_id?: string;
|
|
817
|
+
price_data?: {
|
|
818
|
+
unit_price: number;
|
|
819
|
+
};
|
|
820
|
+
/** Buyer-selected unit price for a product explicitly configured as pay-what-you-want. */
|
|
821
|
+
pay_what_you_want_price?: number;
|
|
822
|
+
title?: string;
|
|
823
|
+
variant_title?: string;
|
|
824
|
+
image_url?: string;
|
|
825
|
+
addon_labels?: string[];
|
|
826
|
+
max_quantity?: number;
|
|
827
|
+
min_quantity?: number;
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* The patch `updateCartItem` accepts, with an explicit TRI-STATE for the two
|
|
831
|
+
* bounds — the same shape `checkout()`'s `affiliateCode` uses for "none":
|
|
832
|
+
*
|
|
833
|
+
* key absent leave the stored bound exactly as it is;
|
|
834
|
+
* `null` DELETE the stored bound (the merchant removed it);
|
|
835
|
+
* `number` set it.
|
|
836
|
+
*
|
|
837
|
+
* Without the `null` arm a bound could be raised and lowered forever but never
|
|
838
|
+
* REMOVED: an update carrying `undefined` is indistinguishable from one that
|
|
839
|
+
* never mentioned the field, so a `max_quantity: 5` the merchant deleted stuck
|
|
840
|
+
* to the line for the life of the cart, and every quote-driven heal that tried
|
|
841
|
+
* to clear it was a silent no-op.
|
|
842
|
+
*/
|
|
843
|
+
type CartItemUpdate = Partial<Omit<CartItem, 'line_id' | 'product_id' | 'variant_id' | 'min_quantity' | 'max_quantity'>> & {
|
|
844
|
+
/** `null` clears the stored minimum. */
|
|
845
|
+
min_quantity?: number | null;
|
|
846
|
+
/** `null` clears the stored ceiling. */
|
|
847
|
+
max_quantity?: number | null;
|
|
848
|
+
};
|
|
849
|
+
interface CartAddOptions {
|
|
850
|
+
addons?: CartAddon[];
|
|
851
|
+
custom_fields?: Record<string, string>;
|
|
852
|
+
price_variant_id?: string;
|
|
853
|
+
price_data?: {
|
|
854
|
+
unit_price: number;
|
|
855
|
+
};
|
|
856
|
+
pay_what_you_want_price?: number;
|
|
857
|
+
title?: string;
|
|
858
|
+
variant_title?: string;
|
|
859
|
+
image_url?: string;
|
|
860
|
+
addon_labels?: string[];
|
|
861
|
+
max_quantity?: number;
|
|
862
|
+
min_quantity?: number;
|
|
863
|
+
replace_display_snapshot?: boolean;
|
|
864
|
+
}
|
|
865
|
+
interface CartPayload {
|
|
866
|
+
store_slug: string;
|
|
867
|
+
items: CartItem[];
|
|
868
|
+
coupon?: string;
|
|
869
|
+
}
|
|
870
|
+
type CartCodeSource = 'COUPON' | 'AFFILIATE';
|
|
871
|
+
interface CartAppliedDiscount {
|
|
872
|
+
source: 'PRODUCT_DISCOUNT' | 'VOLUME_DISCOUNT' | 'AUTOMATIC_PRODUCT_DEAL' | 'BUNDLE_DEAL' | 'STORE_PROMOTION' | 'COUPON' | 'AFFILIATE';
|
|
873
|
+
source_id: string | null;
|
|
874
|
+
source_uniqid: string | null;
|
|
875
|
+
label: string;
|
|
876
|
+
amount: string;
|
|
877
|
+
}
|
|
878
|
+
interface EligibleCartDeal {
|
|
879
|
+
source?: 'AUTOMATIC_PRODUCT_DEAL';
|
|
880
|
+
deal_id: string;
|
|
881
|
+
deal_uniqid: string;
|
|
882
|
+
product_id: string;
|
|
883
|
+
product_uniqid: string | null;
|
|
884
|
+
label: string;
|
|
885
|
+
missing_quantity: number;
|
|
886
|
+
required_quantity: number;
|
|
887
|
+
}
|
|
888
|
+
interface EligibleBundleDealProduct {
|
|
889
|
+
product_id: string;
|
|
890
|
+
product_uniqid: string;
|
|
891
|
+
title: string;
|
|
892
|
+
/** Units to add for this member — its own configured MINIMUM, never a blind 1. */
|
|
893
|
+
quantity: number;
|
|
894
|
+
price: string;
|
|
895
|
+
currency: string;
|
|
896
|
+
/**
|
|
897
|
+
* The option this member is sellable on. A deal is only offered when every member has one, so
|
|
898
|
+
* this is never the "first variant, whatever its stock" it used to be.
|
|
899
|
+
*/
|
|
900
|
+
default_variant_id: string;
|
|
901
|
+
/**
|
|
902
|
+
* The member's order bounds on that option — the merchant's configured range narrowed by what
|
|
903
|
+
* is actually on the shelf. `max_quantity: -1` means nothing bounds the line.
|
|
904
|
+
*
|
|
905
|
+
* OPTIONAL because a server older than this field simply does not send them, and a client
|
|
906
|
+
* cannot invent them: an add against such a payload sends no bounds, exactly as it did before.
|
|
907
|
+
* That is a version gap, not a fallback — the gating that makes the offer safe happens on the
|
|
908
|
+
* server, and a client that guessed bounds here would be inventing the merchant's limits.
|
|
909
|
+
*/
|
|
910
|
+
min_quantity?: number;
|
|
911
|
+
max_quantity?: number;
|
|
912
|
+
}
|
|
913
|
+
interface EligibleBundleDeal {
|
|
914
|
+
source: 'BUNDLE_DEAL';
|
|
915
|
+
bundle_id: string;
|
|
916
|
+
bundle_uniqid: string;
|
|
917
|
+
label: string;
|
|
918
|
+
discount_type: 'PERCENTAGE' | 'FIXED';
|
|
919
|
+
discount_amount: string;
|
|
920
|
+
missing_products: EligibleBundleDealProduct[];
|
|
921
|
+
products: EligibleBundleDealProduct[];
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* THE CART BOUNDS CONTRACT — one quoted line's effective order bounds and sellability.
|
|
925
|
+
*
|
|
926
|
+
* Cart lines carry bounds that were snapshotted when the item was ADDED, so they go stale the
|
|
927
|
+
* moment stock moves or the merchant edits the limits — and a line added before bounds existed
|
|
928
|
+
* carries none at all. The quote is the only thing that runs server-side on every cart change, so
|
|
929
|
+
* it restates them: each settled quote reports the bounds derived from the CURRENT product row and
|
|
930
|
+
* the CURRENT shelf, through the same resolution the checkout refuses with.
|
|
931
|
+
*
|
|
932
|
+
* FIELD CONTRACT:
|
|
933
|
+
* `product_id` the product identifier the cart sent, echoed exactly.
|
|
934
|
+
* `variant_id` the resolved option id, `null` for a product with no variants.
|
|
935
|
+
* `quantity` the quantity this line was quoted at.
|
|
936
|
+
* `min_quantity` ABSENT when it is 1. Present values are the stepper's floor.
|
|
937
|
+
* `max_quantity` ABSENT when nothing bounds the line — absence is the unbounded sentinel, and
|
|
938
|
+
* `-1` is never sent. Never coerce an absent key to 0.
|
|
939
|
+
* SHARED across duplicate lines: cart lines naming the same product and option
|
|
940
|
+
* draw from ONE shelf, and the invoice endpoint sums them and refuses the TOTAL.
|
|
941
|
+
* The value is therefore this line's SHARE — the option's ceiling minus what its
|
|
942
|
+
* sibling lines request. One line per option (the ordinary cart) sees the option
|
|
943
|
+
* ceiling unchanged, and duplicate lines with equal quantities always carry equal
|
|
944
|
+
* bounds. When the siblings have taken the whole ceiling that share is 0 or less,
|
|
945
|
+
* which no per-line bound can express (1 would license a unit that does not exist,
|
|
946
|
+
* 0 is not a quantity a line can hold, absence means unbounded): the line then
|
|
947
|
+
* carries the OPTION'S CEILING — the number the option as a whole must come down
|
|
948
|
+
* to — together with `sellable: false`.
|
|
949
|
+
* `sellable` FALSE when this line cannot be ordered as the cart stands — either no quantity
|
|
950
|
+
* of the option can be ordered at all (empty effective range, empty shelf, nothing
|
|
951
|
+
* to fulfil it), or the cart's lines of this option together request more units
|
|
952
|
+
* than the option's ceiling, which the invoice endpoint refuses on the SUM. The
|
|
953
|
+
* line is still priced and still counted in the totals: it is a line the buyer
|
|
954
|
+
* must resolve by reducing or removing, not one that vanishes. It answers for the
|
|
955
|
+
* OPTION and for the whole cart's demand on it, so every line of one option always
|
|
956
|
+
* reports the same value.
|
|
957
|
+
*
|
|
958
|
+
* CONSUMER DUTY: on every settled quote, REPLACE the stored line bounds with these (do not merge —
|
|
959
|
+
* an absent `max_quantity` means the ceiling was removed), disable `+` at `max_quantity`, hold `-`
|
|
960
|
+
* at `min_quantity`, show a sold-out state for `sellable: false`, and REFUSE THE HAND-OFF while any
|
|
961
|
+
* line is `sellable: false` — that cart is the one the invoice endpoint rejects.
|
|
962
|
+
*
|
|
963
|
+
* OPTIONAL on `CartQuote` because a server older than this field does not send it. A client that
|
|
964
|
+
* receives no `lines` keeps whatever bounds it already had — it must not invent them.
|
|
965
|
+
*/
|
|
966
|
+
interface CartQuoteLine {
|
|
967
|
+
product_id: string;
|
|
968
|
+
variant_id: string | null;
|
|
969
|
+
quantity: number;
|
|
970
|
+
/** Absent when 1. */
|
|
971
|
+
min_quantity?: number;
|
|
972
|
+
/** Absent when unbounded. Never `-1`. */
|
|
973
|
+
max_quantity?: number;
|
|
974
|
+
sellable: boolean;
|
|
975
|
+
}
|
|
976
|
+
interface CartQuote {
|
|
977
|
+
subtotal: string;
|
|
978
|
+
discount: string;
|
|
979
|
+
total: string;
|
|
980
|
+
currency: string;
|
|
981
|
+
/**
|
|
982
|
+
* Opaque server-signed proof of this quote, valid ~15 minutes. `checkout()`
|
|
983
|
+
* submits the most recent one automatically, which is what stops the invoice
|
|
984
|
+
* from being priced above the total shown here. Null when the server has no
|
|
985
|
+
* signing secret configured; absent on older backends.
|
|
986
|
+
*/
|
|
987
|
+
quote_token?: string | null;
|
|
988
|
+
coupon_applied: boolean;
|
|
989
|
+
coupon_source: CartCodeSource | null;
|
|
990
|
+
coupon_discount_order_type: 'TOTAL' | 'PAYMENT_FEES' | null;
|
|
991
|
+
/** Referral code priced into this quote (ambient `?ref=` or a typed referral code). */
|
|
992
|
+
affiliate_code: string | null;
|
|
993
|
+
applied_discounts: CartAppliedDiscount[];
|
|
994
|
+
automatic_product_deals: Array<Record<string, unknown>>;
|
|
995
|
+
bundle_deals: Array<Record<string, unknown>>;
|
|
996
|
+
eligible_deals: EligibleCartDeal[];
|
|
997
|
+
eligible_bundle_deals: EligibleBundleDeal[];
|
|
998
|
+
/** Per-line effective bounds and sellability — see CartQuoteLine. Absent on older backends. */
|
|
999
|
+
lines?: CartQuoteLine[];
|
|
1000
|
+
/**
|
|
1001
|
+
* TRUE when this cart already exceeds the shop's configured checkout maximum.
|
|
1002
|
+
*
|
|
1003
|
+
* Advisory: warn the buyer in the drawer instead of letting them hit the checkout button and
|
|
1004
|
+
* take an unexplained rejection. The server refuses authoritatively at checkout regardless, and
|
|
1005
|
+
* the flag is conservative — it excludes payment fees, so a cart without it can still be
|
|
1006
|
+
* refused. Absent when the cart is within the limit.
|
|
1007
|
+
*/
|
|
1008
|
+
cart_max_exceeded?: true;
|
|
1009
|
+
/** The configured maximum in the quote's currency. Present only alongside `cart_max_exceeded`. */
|
|
1010
|
+
cart_maximum_checkout?: string;
|
|
1011
|
+
}
|
|
1012
|
+
interface CartMetadata {
|
|
1013
|
+
created_at: number;
|
|
1014
|
+
last_modified: number;
|
|
1015
|
+
version: number;
|
|
1016
|
+
checksum: string;
|
|
1017
|
+
}
|
|
1018
|
+
interface CartStats {
|
|
1019
|
+
item_count: number;
|
|
1020
|
+
total_quantity: number;
|
|
1021
|
+
last_modified: number;
|
|
1022
|
+
version: number;
|
|
1023
|
+
has_backup: boolean;
|
|
1024
|
+
integrity_valid: boolean;
|
|
1025
|
+
total_price: number;
|
|
1026
|
+
total_price_is_estimate: boolean;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* SDK Configuration Management
|
|
1031
|
+
*/
|
|
1032
|
+
|
|
1033
|
+
declare function getConfig(): ShoppexConfig;
|
|
1034
|
+
declare function isInitialized(): boolean;
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* Typed OpenAPI client factory.
|
|
1038
|
+
*
|
|
1039
|
+
* Surfaces the generated `createApiClient` from `@shoppex/contracts` with the
|
|
1040
|
+
* SDK's configured `apiBaseUrl`. This lets consumers of `@shoppexio/storefront`
|
|
1041
|
+
* call any public Dev API endpoint with full end-to-end types without
|
|
1042
|
+
* rebuilding a client from scratch.
|
|
1043
|
+
*
|
|
1044
|
+
* Prefer the high-level modules (`shoppex.getProducts()` etc.) for common
|
|
1045
|
+
* read flows — they handle caching, pagination defaults, and storefront
|
|
1046
|
+
* scoping. Drop down to `shoppex.client()` only when you need an endpoint
|
|
1047
|
+
* the high-level API does not cover yet.
|
|
1048
|
+
*/
|
|
1049
|
+
|
|
1050
|
+
type ApiClient = ReturnType<typeof createApiClient>;
|
|
1051
|
+
/**
|
|
1052
|
+
* Return a typed OpenAPI client bound to the SDK's configured API base URL.
|
|
1053
|
+
* The client is cached per base URL and recreated when the SDK is re-initialized
|
|
1054
|
+
* against a different host.
|
|
1055
|
+
*/
|
|
1056
|
+
declare function getTypedClient(token?: string): ApiClient;
|
|
1057
|
+
|
|
1058
|
+
/**
|
|
1059
|
+
* SDK Error Classes
|
|
1060
|
+
*/
|
|
1061
|
+
declare class ShoppexError extends Error {
|
|
1062
|
+
readonly code: string;
|
|
1063
|
+
readonly statusCode?: number;
|
|
1064
|
+
constructor(message: string, code: string, statusCode?: number);
|
|
1065
|
+
}
|
|
1066
|
+
declare class NotInitializedError extends ShoppexError {
|
|
1067
|
+
constructor();
|
|
1068
|
+
}
|
|
1069
|
+
declare class NetworkError extends ShoppexError {
|
|
1070
|
+
constructor(message: string, statusCode?: number);
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* A refusal the SERVER made and named. `code` is the backend's `error_code`
|
|
1074
|
+
* (e.g. `errors.checkout.coupon_no_longer_valid`) — the branchable identity of
|
|
1075
|
+
* the refusal, stable across locales, unlike the localized `message`.
|
|
1076
|
+
*
|
|
1077
|
+
* Distinct from {@link NetworkError}, whose `code` is always `NETWORK_ERROR`:
|
|
1078
|
+
* that one means "the request did not produce a named answer".
|
|
1079
|
+
*/
|
|
1080
|
+
declare class ApiError extends ShoppexError {
|
|
1081
|
+
readonly errorParams?: Record<string, unknown>;
|
|
1082
|
+
constructor(message: string, code: string, statusCode?: number, errorParams?: Record<string, unknown>);
|
|
1083
|
+
}
|
|
1084
|
+
declare class ValidationError extends ShoppexError {
|
|
1085
|
+
readonly invalidFields?: string[];
|
|
1086
|
+
constructor(message: string, invalidFields?: string[]);
|
|
1087
|
+
}
|
|
1088
|
+
declare class CartError extends ShoppexError {
|
|
1089
|
+
constructor(message: string);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
interface StorefrontCustomField {
|
|
1093
|
+
name: string;
|
|
1094
|
+
type: string;
|
|
1095
|
+
required: boolean;
|
|
1096
|
+
defaultValue: string;
|
|
1097
|
+
placeholder: string;
|
|
1098
|
+
regex?: string;
|
|
1099
|
+
}
|
|
1100
|
+
declare function normalizeStorefrontCustomFields(raw: unknown): StorefrontCustomField[];
|
|
1101
|
+
declare function isStorefrontCheckboxCustomFieldValueChecked(value: string | undefined): boolean;
|
|
1102
|
+
declare function validateStorefrontCustomFieldValue(field: StorefrontCustomField, value: string): string | null;
|
|
1103
|
+
declare function buildStorefrontCustomFieldPayload(fields: StorefrontCustomField[], values: Record<string, string>): Record<string, string>;
|
|
1104
|
+
|
|
1105
|
+
type VariantLike = Pick<ProductVariant, 'stock' | 'orderable' | 'supplier_backed'> | Pick<PriceVariant, 'stock' | 'orderable' | 'supplier_backed'>;
|
|
1106
|
+
type ProductLike = Pick<Product, 'type' | 'stock' | 'orderable' | 'supplier_backed' | 'on_hold' | 'price_variants' | 'variants'> & {
|
|
1107
|
+
onHold?: boolean | number | string | null;
|
|
1108
|
+
is_on_hold?: boolean | number | string | null;
|
|
1109
|
+
isOnHold?: boolean | number | string | null;
|
|
1110
|
+
};
|
|
1111
|
+
declare function resolveVariantStockValue(variant: VariantLike | null | undefined): number;
|
|
1112
|
+
declare function resolveDisplayStock(product: ProductLike | null | undefined): number;
|
|
1113
|
+
declare function isProductOutOfStock(product: ProductLike | null | undefined): boolean;
|
|
1114
|
+
declare function isProductInStock(product: ProductLike | null | undefined): boolean;
|
|
1115
|
+
declare function isVariantOutOfStock(variant: VariantLike | null | undefined): boolean;
|
|
1116
|
+
|
|
1117
|
+
declare function buildStorefrontProductLookup(products?: Product[]): Map<string, Product>;
|
|
1118
|
+
declare function getStorefrontGroupProducts(group: ProductGroup, productsOrLookup?: Product[] | Map<string, Product>): Product[];
|
|
1119
|
+
declare function getMergedStorefrontProducts(products?: Product[]): Product[];
|
|
1120
|
+
|
|
1121
|
+
interface StorefrontSearchFilterOptions {
|
|
1122
|
+
hideOutOfStock?: boolean;
|
|
1123
|
+
maxResults?: number;
|
|
1124
|
+
}
|
|
1125
|
+
type StorefrontCatalogSearchItem = {
|
|
1126
|
+
type: 'product';
|
|
1127
|
+
product: Product;
|
|
1128
|
+
} | {
|
|
1129
|
+
type: 'group';
|
|
1130
|
+
group: ProductGroup;
|
|
1131
|
+
products: Product[];
|
|
1132
|
+
};
|
|
1133
|
+
declare function stripHtmlFromText(value: string | null | undefined): string;
|
|
1134
|
+
declare function normalizeSearchQuery(query: string): string;
|
|
1135
|
+
declare function collectProductSearchHaystack(product: Product): string[];
|
|
1136
|
+
declare function productMatchesSearchQuery(product: Product, query: string): boolean;
|
|
1137
|
+
declare function groupMatchesSearchQuery(group: ProductGroup, query: string): boolean;
|
|
1138
|
+
declare function filterProductsBySearchQuery(products: Product[], query: string, options?: StorefrontSearchFilterOptions): Product[];
|
|
1139
|
+
declare function searchMergedStorefrontCatalogItems(products: Product[], groups: ProductGroup[], query: string, options?: StorefrontSearchFilterOptions): StorefrontCatalogSearchItem[];
|
|
1140
|
+
declare function searchMergedStorefrontCatalog(products: Product[], groups: ProductGroup[], query: string, options?: StorefrontSearchFilterOptions): Product[];
|
|
1141
|
+
|
|
1142
|
+
type StorefrontContactTicketInput = {
|
|
1143
|
+
shopSlug: string;
|
|
1144
|
+
email: string;
|
|
1145
|
+
message: string;
|
|
1146
|
+
title?: string;
|
|
1147
|
+
name?: string;
|
|
1148
|
+
invoiceId?: string;
|
|
1149
|
+
apiBaseUrl?: string;
|
|
1150
|
+
};
|
|
1151
|
+
type StorefrontContactTicketResult = {
|
|
1152
|
+
uniqid: string;
|
|
1153
|
+
};
|
|
1154
|
+
declare function resolveStorefrontApiBaseUrl(options?: {
|
|
1155
|
+
apiBaseUrl?: string;
|
|
1156
|
+
hostname?: string;
|
|
1157
|
+
}): string;
|
|
1158
|
+
declare function buildStorefrontContactMessage(input: {
|
|
1159
|
+
name?: string;
|
|
1160
|
+
message: string;
|
|
1161
|
+
maxLength?: number;
|
|
1162
|
+
}): string;
|
|
1163
|
+
declare function submitStorefrontContactTicket(input: StorefrontContactTicketInput): Promise<StorefrontContactTicketResult>;
|
|
1164
|
+
type StorefrontSocialLinks = {
|
|
1165
|
+
discord?: string | null;
|
|
1166
|
+
telegram?: string | null;
|
|
1167
|
+
};
|
|
1168
|
+
declare function resolveStorefrontSocialLinks(store: {
|
|
1169
|
+
discord_link?: string | null;
|
|
1170
|
+
telegram_link?: string | null;
|
|
1171
|
+
social?: Record<string, string | null | undefined> | null;
|
|
1172
|
+
} | null | undefined): StorefrontSocialLinks;
|
|
1173
|
+
|
|
1174
|
+
/**
|
|
1175
|
+
* Theme configuration types (framework agnostic)
|
|
1176
|
+
*/
|
|
1177
|
+
type SettingFieldType = 'color' | 'text' | 'range' | 'select' | 'boolean' | 'image' | 'font' | 'richtext' | 'products' | 'images';
|
|
1178
|
+
interface BaseSettingField {
|
|
1179
|
+
type: SettingFieldType;
|
|
1180
|
+
label: string;
|
|
1181
|
+
description?: string;
|
|
1182
|
+
}
|
|
1183
|
+
interface ColorField extends BaseSettingField {
|
|
1184
|
+
type: 'color';
|
|
1185
|
+
default: string;
|
|
1186
|
+
}
|
|
1187
|
+
interface RangeField extends BaseSettingField {
|
|
1188
|
+
type: 'range';
|
|
1189
|
+
default: number;
|
|
1190
|
+
min: number;
|
|
1191
|
+
max: number;
|
|
1192
|
+
step?: number;
|
|
1193
|
+
unit?: string;
|
|
1194
|
+
}
|
|
1195
|
+
interface SelectField extends BaseSettingField {
|
|
1196
|
+
type: 'select';
|
|
1197
|
+
default: string;
|
|
1198
|
+
options: string[] | {
|
|
1199
|
+
value: string;
|
|
1200
|
+
label: string;
|
|
1201
|
+
}[];
|
|
1202
|
+
}
|
|
1203
|
+
interface BooleanField extends BaseSettingField {
|
|
1204
|
+
type: 'boolean';
|
|
1205
|
+
default: boolean;
|
|
1206
|
+
}
|
|
1207
|
+
interface TextField extends BaseSettingField {
|
|
1208
|
+
type: 'text';
|
|
1209
|
+
default: string;
|
|
1210
|
+
}
|
|
1211
|
+
interface ImageField extends BaseSettingField {
|
|
1212
|
+
type: 'image';
|
|
1213
|
+
default: string | null;
|
|
1214
|
+
}
|
|
1215
|
+
interface FontField extends BaseSettingField {
|
|
1216
|
+
type: 'font';
|
|
1217
|
+
default: string;
|
|
1218
|
+
options?: string[];
|
|
1219
|
+
}
|
|
1220
|
+
interface RichtextField extends BaseSettingField {
|
|
1221
|
+
type: 'richtext';
|
|
1222
|
+
default: string;
|
|
1223
|
+
}
|
|
1224
|
+
interface ProductsField extends BaseSettingField {
|
|
1225
|
+
type: 'products';
|
|
1226
|
+
default: string[];
|
|
1227
|
+
max?: number;
|
|
1228
|
+
}
|
|
1229
|
+
interface ImagesField extends BaseSettingField {
|
|
1230
|
+
type: 'images';
|
|
1231
|
+
default: string[];
|
|
1232
|
+
max?: number;
|
|
1233
|
+
}
|
|
1234
|
+
type SettingField = ColorField | RangeField | SelectField | BooleanField | TextField | ImageField | FontField | RichtextField | ProductsField | ImagesField;
|
|
1235
|
+
interface SectionDefinition {
|
|
1236
|
+
name: string;
|
|
1237
|
+
description?: string;
|
|
1238
|
+
icon?: string;
|
|
1239
|
+
settings: Record<string, SettingField>;
|
|
1240
|
+
}
|
|
1241
|
+
interface BlockDefinition {
|
|
1242
|
+
name: string;
|
|
1243
|
+
description?: string;
|
|
1244
|
+
icon?: string;
|
|
1245
|
+
settings: Record<string, SettingField>;
|
|
1246
|
+
allowedPages?: string[];
|
|
1247
|
+
maxPerPage?: number;
|
|
1248
|
+
}
|
|
1249
|
+
interface BlockInstance {
|
|
1250
|
+
id: string;
|
|
1251
|
+
type: string;
|
|
1252
|
+
visible?: boolean;
|
|
1253
|
+
settings?: Record<string, SettingValue>;
|
|
1254
|
+
}
|
|
1255
|
+
interface PageLayout {
|
|
1256
|
+
blocks: BlockInstance[];
|
|
1257
|
+
}
|
|
1258
|
+
interface ThemeBlockManifest {
|
|
1259
|
+
pages: string[];
|
|
1260
|
+
blocks: Record<string, BlockDefinition>;
|
|
1261
|
+
defaultLayouts?: Record<string, PageLayout>;
|
|
1262
|
+
}
|
|
1263
|
+
interface ThemeConfig {
|
|
1264
|
+
id: string;
|
|
1265
|
+
name: string;
|
|
1266
|
+
description?: string;
|
|
1267
|
+
version: string;
|
|
1268
|
+
author?: string;
|
|
1269
|
+
preview?: string;
|
|
1270
|
+
settings: Record<string, Record<string, SettingField>>;
|
|
1271
|
+
sections: Record<string, SectionDefinition>;
|
|
1272
|
+
blocks?: Record<string, BlockDefinition>;
|
|
1273
|
+
pageLayouts?: Record<string, PageLayout>;
|
|
1274
|
+
builder?: ThemeBlockManifest;
|
|
1275
|
+
}
|
|
1276
|
+
type SettingValue = string | number | boolean | null | string[] | Record<string, unknown>;
|
|
1277
|
+
type ResolvedThemeSettings = {
|
|
1278
|
+
[Category: string]: {
|
|
1279
|
+
[Key: string]: SettingValue;
|
|
1280
|
+
};
|
|
1281
|
+
};
|
|
1282
|
+
|
|
1283
|
+
type JsonRecord = Record<string, unknown>;
|
|
1284
|
+
interface PublishedBuilderSettings {
|
|
1285
|
+
version: number;
|
|
1286
|
+
revision: number;
|
|
1287
|
+
theme: {
|
|
1288
|
+
content: JsonRecord;
|
|
1289
|
+
layout: JsonRecord;
|
|
1290
|
+
style_slots: JsonRecord;
|
|
1291
|
+
pages?: unknown[];
|
|
1292
|
+
terms?: JsonRecord;
|
|
1293
|
+
[key: string]: unknown;
|
|
1294
|
+
};
|
|
1295
|
+
[key: string]: unknown;
|
|
1296
|
+
}
|
|
1297
|
+
interface PublishedThemeSettingsPayload {
|
|
1298
|
+
settings: ResolvedThemeSettings;
|
|
1299
|
+
builder_settings: PublishedBuilderSettings | null;
|
|
1300
|
+
content: JsonRecord;
|
|
1301
|
+
style_slots: JsonRecord;
|
|
1302
|
+
}
|
|
1303
|
+
declare function fetchPublishedBuilderSettings(shopSlug: string): Promise<PublishedThemeSettingsPayload | null>;
|
|
1304
|
+
declare function fetchPublishedThemeSettings(shopSlug: string): Promise<ResolvedThemeSettings | null>;
|
|
1305
|
+
declare function resolveDefaults(config: ThemeConfig): ResolvedThemeSettings;
|
|
1306
|
+
declare function mergeSettings(defaults: ResolvedThemeSettings, overrides: Partial<ResolvedThemeSettings>): ResolvedThemeSettings;
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
* Store Module
|
|
1310
|
+
*
|
|
1311
|
+
* API methods for store data.
|
|
1312
|
+
*/
|
|
1313
|
+
|
|
1314
|
+
interface GetStorefrontOptions {
|
|
1315
|
+
productsLimit?: number;
|
|
1316
|
+
productsCursor?: string | null;
|
|
1317
|
+
}
|
|
1318
|
+
declare function getStore(): Promise<SDKResponse<Shop>>;
|
|
1319
|
+
declare function resolveStoreByDomain(domain?: string, apiBaseUrl?: string): Promise<SDKResponse<Shop>>;
|
|
1320
|
+
declare function getStorefront(options?: GetStorefrontOptions): Promise<SDKResponse<StorefrontData>>;
|
|
1321
|
+
declare function getStoreLogoUrl(): Promise<string | null>;
|
|
1322
|
+
declare function getStoreBannerUrl(): Promise<string | null>;
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
* Products Module
|
|
1326
|
+
*
|
|
1327
|
+
* API methods for product data.
|
|
1328
|
+
*/
|
|
1329
|
+
|
|
1330
|
+
interface GetStorefrontProductsPageOptions {
|
|
1331
|
+
cursor?: string | null;
|
|
1332
|
+
limit?: number;
|
|
1333
|
+
sort?: 'featured' | 'newest' | 'price-asc' | 'price-desc' | string | null;
|
|
1334
|
+
category?: string | null;
|
|
1335
|
+
hideOutOfStock?: boolean;
|
|
1336
|
+
}
|
|
1337
|
+
declare function getProducts(): Promise<SDKResponse<Product[]>>;
|
|
1338
|
+
declare function getStorefrontProductsPage(options?: GetStorefrontProductsPageOptions): Promise<SDKResponse<{
|
|
1339
|
+
products: Product[];
|
|
1340
|
+
pagination: CursorPagination | null;
|
|
1341
|
+
}>>;
|
|
1342
|
+
declare function getProduct(idOrSlug: string): Promise<SDKResponse<Product>>;
|
|
1343
|
+
declare function getCategories(): Promise<SDKResponse<string[]>>;
|
|
1344
|
+
|
|
1345
|
+
declare function getCartCoupon(): string | null;
|
|
1346
|
+
declare function getCartCouponSource(): CartCodeSource | null;
|
|
1347
|
+
declare function setCartCoupon(coupon: string | null | undefined, source?: CartCodeSource): string | null;
|
|
1348
|
+
declare function clearCartCoupon(): void;
|
|
1349
|
+
declare function getCart(): CartItem[];
|
|
1350
|
+
/** Resolve a cart line_id for product+variant. When multiple configs exist, returns the first line. */
|
|
1351
|
+
declare function resolveCartLineId(productId: string, variantId: string, cart?: CartItem[]): string;
|
|
1352
|
+
declare function getCartItemCount(): number;
|
|
1353
|
+
declare function addToCart(productId: string, variantId: string, quantity?: number, options?: CartAddOptions): void;
|
|
1354
|
+
declare function setCartItem(productId: string, variantId: string, quantity?: number, options?: CartAddOptions): void;
|
|
1355
|
+
/**
|
|
1356
|
+
* Patch one cart line.
|
|
1357
|
+
*
|
|
1358
|
+
* TRI-STATE BOUNDS: `min_quantity`/`max_quantity` accept `null` to DELETE the
|
|
1359
|
+
* stored bound — see {@link CartItemUpdate}. `undefined` (or an absent key)
|
|
1360
|
+
* leaves it alone, which is why a removed ceiling needs its own spelling.
|
|
1361
|
+
*
|
|
1362
|
+
* NO-OP UPDATES DO NOT WRITE. An update that leaves the cart byte-identical
|
|
1363
|
+
* returns without touching storage: no metadata bump, no `version` increment,
|
|
1364
|
+
* and — because the storage write is what other tabs observe — no `storage`
|
|
1365
|
+
* event. Writing anyway made an ineffective heal indistinguishable from a real
|
|
1366
|
+
* cart change, and two tabs healing the same line could hand the event back and
|
|
1367
|
+
* forth indefinitely. Line-shape healing of a legacy stored row is NOT this
|
|
1368
|
+
* function's job: `normalizeCartItems` clamps and repairs on every READ, which
|
|
1369
|
+
* is the chokepoint every access already flows through.
|
|
1370
|
+
*/
|
|
1371
|
+
declare function updateCartItem(lineId: string, updates: CartItemUpdate): void;
|
|
1372
|
+
declare function removeFromCart(lineId: string): void;
|
|
1373
|
+
/**
|
|
1374
|
+
* Discards the cart entirely — and with it the proof that describes it.
|
|
1375
|
+
*
|
|
1376
|
+
* PAYMENT PATH. `latestQuoteToken` is evidence about a specific cart, and this
|
|
1377
|
+
* is the one mutation after which that cart does not exist in any form. Held
|
|
1378
|
+
* across the clear, the proof outlives its subject: a caller that clears and
|
|
1379
|
+
* re-adds (Buy Now REPLACES the cart) hands `/from-cart` a token bound to the
|
|
1380
|
+
* previous cart's hash, which is refused as `quote_token_stale` — a hand-off
|
|
1381
|
+
* dead-ended by a proof nobody asked for. And with no re-quote in between there
|
|
1382
|
+
* is nothing to replace it with, so the retry is refused identically.
|
|
1383
|
+
*
|
|
1384
|
+
* Only this path clears it. Every OTHER mutation turns one cart into another
|
|
1385
|
+
* cart, where a proof that no longer matches is the server's to refuse: it
|
|
1386
|
+
* answers `quote_token_stale`, the surfaces re-quote, and the buyer approves the
|
|
1387
|
+
* new total. Dropping the proof there instead would silently remove the approved
|
|
1388
|
+
* -total ceiling from a checkout that raced the re-quote, which is the failure
|
|
1389
|
+
* the token exists to prevent.
|
|
1390
|
+
*/
|
|
1391
|
+
declare function clearCart(): void;
|
|
1392
|
+
declare function createCartBackup(): void;
|
|
1393
|
+
declare function restoreCartFromBackup(): boolean;
|
|
1394
|
+
declare function mergeBaskets(items: CartBasketMergeLine[]): CartItem[];
|
|
1395
|
+
declare function moveBasketItem(fromProductId: string, fromVariantId: string, toProductId: string, toVariantId: string): void;
|
|
1396
|
+
declare function validateCartIntegrity(): boolean;
|
|
1397
|
+
declare function getCartStats(): CartStats;
|
|
1398
|
+
declare function quoteCart(coupon?: string, currency?: string): Promise<src.SDKResponse<CartQuote>>;
|
|
1399
|
+
|
|
1400
|
+
type CartLineIdentityInput = Pick<CartItem, 'product_id' | 'variant_id' | 'price_variant_id' | 'addons' | 'custom_fields' | 'price_data' | 'pay_what_you_want_price'>;
|
|
1401
|
+
declare function computeCartLineId(input: CartLineIdentityInput): string;
|
|
1402
|
+
declare function ensureCartLineId(item: CartItem): CartItem;
|
|
1403
|
+
|
|
1404
|
+
/**
|
|
1405
|
+
* Checkout Module
|
|
1406
|
+
*
|
|
1407
|
+
* Creates invoice via backend API and redirects to hosted checkout page.
|
|
1408
|
+
* All checkout flows go through: checkout.shoppex.io/invoice/{invoiceId}
|
|
1409
|
+
*/
|
|
1410
|
+
|
|
1411
|
+
interface CheckoutOptions {
|
|
1412
|
+
autoRedirect?: boolean;
|
|
1413
|
+
locale?: string;
|
|
1414
|
+
email?: string;
|
|
1415
|
+
coupon?: string;
|
|
1416
|
+
currency?: string;
|
|
1417
|
+
/**
|
|
1418
|
+
* Absolute HTTPS URL used after a completed purchase when the product does
|
|
1419
|
+
* not define its own redirect. This is persisted on the invoice, so each
|
|
1420
|
+
* sales channel can provide its own return destination.
|
|
1421
|
+
*/
|
|
1422
|
+
redirectUrl?: string;
|
|
1423
|
+
/**
|
|
1424
|
+
* A Cloudflare Turnstile proof for the `storefront_checkout` action.
|
|
1425
|
+
*
|
|
1426
|
+
* Normally omitted. A checkout surface may provide a renewed proof when a
|
|
1427
|
+
* legitimate shared network reaches the public invoice-create budget. The
|
|
1428
|
+
* widget must run on the same origin that calls this SDK, while the proof is
|
|
1429
|
+
* transport-only and does not change the checkout intent or its idempotency
|
|
1430
|
+
* key.
|
|
1431
|
+
*/
|
|
1432
|
+
turnstileToken?: string;
|
|
1433
|
+
/**
|
|
1434
|
+
* Referral (affiliate) code to submit, as a tri-state:
|
|
1435
|
+
*
|
|
1436
|
+
* - `undefined` — the SDK resolves it from storage (cart-applied referral
|
|
1437
|
+
* code first, then the ambient `?ref=` attribution).
|
|
1438
|
+
* - `''` — explicitly none: submit no referral code and do NOT re-read
|
|
1439
|
+
* storage. This is how a caller pins "quoted WITHOUT a referral"; without
|
|
1440
|
+
* it a code captured between the quote and the submit would be applied to
|
|
1441
|
+
* a total the buyer never saw.
|
|
1442
|
+
* - non-empty — pinned: submit exactly this code.
|
|
1443
|
+
*
|
|
1444
|
+
* `referralCode` is an alias and follows the same rules; `affiliateCode`
|
|
1445
|
+
* wins when both are present.
|
|
1446
|
+
*/
|
|
1447
|
+
affiliateCode?: string;
|
|
1448
|
+
/** Alias of {@link CheckoutOptions.affiliateCode}, same tri-state. */
|
|
1449
|
+
referralCode?: string;
|
|
1450
|
+
customerCheckoutPath?: '/dashboard/api/checkout';
|
|
1451
|
+
/**
|
|
1452
|
+
* The cart this hand-off was approved FOR, as `JSON.stringify(getCart())`.
|
|
1453
|
+
*
|
|
1454
|
+
* PAYMENT PATH. A caller that writes the cart and reads it back before
|
|
1455
|
+
* checking out (Buy Now, which replaces the cart, is the sharp case) verifies
|
|
1456
|
+
* a cart it read at one moment, while these functions read storage again on
|
|
1457
|
+
* their way into the request — and localStorage is shared with every other
|
|
1458
|
+
* tab on the domain. A write landing in that gap is billed without ever
|
|
1459
|
+
* having passed the caller's verification.
|
|
1460
|
+
*
|
|
1461
|
+
* The caller cannot close that itself: any check it makes is one more read
|
|
1462
|
+
* BEFORE this one. Pinning the expected bytes moves the comparison to the
|
|
1463
|
+
* only place it can be conclusive — against the exact cart the call is about
|
|
1464
|
+
* to POST. A mismatch refuses before anything is created or sent.
|
|
1465
|
+
*
|
|
1466
|
+
* Honoured identically by BOTH invoice-creating entry points, `checkout()`
|
|
1467
|
+
* and `buildCheckoutUrl()` — they hit the same endpoint and create the same
|
|
1468
|
+
* invoice, so a pin that held on one and not the other would be a promise
|
|
1469
|
+
* the caller could not rely on. Only the refusal shape differs, following
|
|
1470
|
+
* each function's own success shape: `checkout()` returns
|
|
1471
|
+
* `{ success: false }`, `buildCheckoutUrl()` throws.
|
|
1472
|
+
*
|
|
1473
|
+
* Optional and additive: omitted, nothing changes, and the server is not
|
|
1474
|
+
* involved either way.
|
|
1475
|
+
*/
|
|
1476
|
+
expectedCart?: string;
|
|
1477
|
+
}
|
|
1478
|
+
interface CheckoutResult {
|
|
1479
|
+
success: boolean;
|
|
1480
|
+
redirectUrl?: string;
|
|
1481
|
+
invoiceId?: string;
|
|
1482
|
+
rewards?: BuyerRewards | null;
|
|
1483
|
+
message?: string;
|
|
1484
|
+
/**
|
|
1485
|
+
* The server's machine-readable refusal identifier when the checkout was
|
|
1486
|
+
* refused, e.g. `errors.checkout.price_increased_since_quote` or
|
|
1487
|
+
* `errors.checkout.affiliate_code_not_applicable`. Branch on this rather
|
|
1488
|
+
* than on `message`, which is localized display copy.
|
|
1489
|
+
*
|
|
1490
|
+
* The quote-proof family — `errors.checkout.quote_token_invalid`,
|
|
1491
|
+
* `errors.checkout.quote_token_expired`, `errors.checkout.quote_token_stale`
|
|
1492
|
+
* — says the proof this call submitted could not be honoured: not ours, past
|
|
1493
|
+
* its window, or priced for a cart/currency/codes that have since moved. The
|
|
1494
|
+
* server refuses rather than pricing without the ceiling, so the caller's
|
|
1495
|
+
* response is always the same: `quoteCart()` again, show the buyer the new
|
|
1496
|
+
* total, and only then retry. A retry with the same stale token is refused
|
|
1497
|
+
* identically.
|
|
1498
|
+
*
|
|
1499
|
+
* Absent for failures the server never named (transport, timeout, an empty
|
|
1500
|
+
* cart caught client-side).
|
|
1501
|
+
*/
|
|
1502
|
+
code?: string;
|
|
1503
|
+
/**
|
|
1504
|
+
* Human-verification challenge returned when a legitimate shared network
|
|
1505
|
+
* reaches the invoice-create budget. Render the named provider on the same
|
|
1506
|
+
* origin that calls the SDK, then retry with its proof as `turnstileToken`.
|
|
1507
|
+
*/
|
|
1508
|
+
challenge?: ApiChallenge;
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Typed refusal from {@link buildCheckoutUrl}. The function keeps its existing
|
|
1512
|
+
* throw-based contract while exposing a server-requested human-verification
|
|
1513
|
+
* challenge so callers can render it and retry with `turnstileToken`.
|
|
1514
|
+
*/
|
|
1515
|
+
declare class CheckoutCreateError extends Error {
|
|
1516
|
+
readonly challenge?: ApiChallenge;
|
|
1517
|
+
readonly code?: string;
|
|
1518
|
+
readonly status?: number;
|
|
1519
|
+
constructor(message: string, options?: {
|
|
1520
|
+
challenge?: ApiChallenge;
|
|
1521
|
+
code?: string;
|
|
1522
|
+
status?: number;
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
declare function checkout(couponOrOptions?: string | CheckoutOptions, options?: CheckoutOptions): Promise<CheckoutResult>;
|
|
1526
|
+
/**
|
|
1527
|
+
* Build checkout URL by creating invoice first.
|
|
1528
|
+
* Returns the checkout URL for the created invoice.
|
|
1529
|
+
*
|
|
1530
|
+
* PAYMENT PATH. "Build a URL" names what this returns, not a lighter way to
|
|
1531
|
+
* price a cart: it POSTs the same invoice-creating endpoint as `checkout()` and
|
|
1532
|
+
* bills whatever storage holds when it reads. It therefore honours
|
|
1533
|
+
* `CheckoutOptions.expectedCart` on exactly the same terms — a pinned cart that
|
|
1534
|
+
* moved is refused, not invoiced.
|
|
1535
|
+
*
|
|
1536
|
+
* A refusal throws, like every other refusal on this function: the success
|
|
1537
|
+
* shape is a URL string, so there is no in-band value that a caller could
|
|
1538
|
+
* mistake for one. The thrown message is the buyer-facing copy, identical to
|
|
1539
|
+
* the sentence `checkout()` returns, and callers that already handle the
|
|
1540
|
+
* `Cart is empty` throw handle this one unchanged.
|
|
1541
|
+
*/
|
|
1542
|
+
declare function buildCheckoutUrl(couponOrOptions?: string | CheckoutOptions, options?: CheckoutOptions): Promise<string>;
|
|
1543
|
+
/**
|
|
1544
|
+
* @deprecated Use buildCheckoutUrl instead.
|
|
1545
|
+
* Sync version is no longer supported as invoice creation requires API call.
|
|
1546
|
+
*/
|
|
1547
|
+
declare function buildCheckoutUrlSync(): never;
|
|
1548
|
+
|
|
1549
|
+
interface SearchOptions {
|
|
1550
|
+
hideOutOfStock?: boolean;
|
|
1551
|
+
maxResults?: number;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
declare function searchCatalogItems(query: string, options?: SearchOptions): Promise<SDKResponse<StorefrontCatalogSearchItem[]>>;
|
|
1555
|
+
declare function searchProducts(query: string, options?: SearchOptions): Promise<SDKResponse<Product[]>>;
|
|
1556
|
+
|
|
1557
|
+
declare function setAffiliateCode(code: string | null | undefined, ttlDays?: number): string | null;
|
|
1558
|
+
declare function clearAffiliateCode(): void;
|
|
1559
|
+
declare function getAffiliateCode(): string | null;
|
|
1560
|
+
declare function trackAffiliateEvent(eventType: 'add_to_cart' | 'checkout_started', options?: {
|
|
1561
|
+
/**
|
|
1562
|
+
* The code the surrounding call actually resolved and submitted (e.g.
|
|
1563
|
+
* checkout()'s tri-state result). `null` means the caller explicitly
|
|
1564
|
+
* submitted WITHOUT a referral — no event is recorded, so an ambient
|
|
1565
|
+
* stored code is never credited for a sale it did not get. Omit the
|
|
1566
|
+
* options object entirely to attribute to the stored ambient code.
|
|
1567
|
+
*/
|
|
1568
|
+
code: string | null;
|
|
1569
|
+
/**
|
|
1570
|
+
* Overrides the per-browser-session dedupe key. checkout entry points
|
|
1571
|
+
* pass `inv:<invoiceId>` so every created invoice counts as exactly one
|
|
1572
|
+
* checkout — a session-wide key would drop the second checkout of a
|
|
1573
|
+
* buyer who orders twice in one session while both sales still count.
|
|
1574
|
+
*/
|
|
1575
|
+
dedupeKey?: string;
|
|
1576
|
+
}): Promise<void>;
|
|
1577
|
+
declare function validateAffiliateCode(code: string): Promise<SDKResponse<AffiliateValidation>>;
|
|
1578
|
+
declare function applyAffiliateCode(code: string): Promise<SDKResponse<AffiliateValidation>>;
|
|
1579
|
+
/**
|
|
1580
|
+
* Capture an affiliate code from the current URL and store it for 30 days (last-click).
|
|
1581
|
+
*
|
|
1582
|
+
* Example:
|
|
1583
|
+
* - URL: https://mystore.com/product/abc?ref=deadbeef
|
|
1584
|
+
* - captureAffiliateFromUrl() stores "deadbeef" and returns it.
|
|
1585
|
+
*/
|
|
1586
|
+
declare function captureAffiliateFromUrl(param?: string): Promise<string | null>;
|
|
1587
|
+
|
|
1588
|
+
/**
|
|
1589
|
+
* Coupons Module
|
|
1590
|
+
*
|
|
1591
|
+
* Coupon validation before checkout.
|
|
1592
|
+
*/
|
|
1593
|
+
|
|
1594
|
+
declare function validateCoupon(code: string, productOrOptions?: string | CouponValidationOptions): Promise<SDKResponse<CouponValidation>>;
|
|
1595
|
+
|
|
1596
|
+
/**
|
|
1597
|
+
* Reviews Module
|
|
1598
|
+
*
|
|
1599
|
+
* Shop-level feedback/reviews.
|
|
1600
|
+
* Note: Shoppex has shop-level feedback, not product-level reviews.
|
|
1601
|
+
*/
|
|
1602
|
+
|
|
1603
|
+
declare function getShopReviewsPage(cursor?: string | null): Promise<SDKResponse<ShopReviewsPage>>;
|
|
1604
|
+
declare function getShopReviews(): Promise<SDKResponse<Feedback[]>>;
|
|
1605
|
+
|
|
1606
|
+
/**
|
|
1607
|
+
* Customer Module
|
|
1608
|
+
*
|
|
1609
|
+
* Authenticated customer account calls for code-lane storefronts. The edge
|
|
1610
|
+
* worker owns the HttpOnly session cookie and derives the shop from the host.
|
|
1611
|
+
* This module therefore sends neither a session token nor shop identity.
|
|
1612
|
+
*/
|
|
1613
|
+
|
|
1614
|
+
interface CustomerOrdersQuery {
|
|
1615
|
+
page?: number;
|
|
1616
|
+
limit?: number;
|
|
1617
|
+
/**
|
|
1618
|
+
* Invoice status filter, verbatim from the producer's enum. Anything else is
|
|
1619
|
+
* answered with a 400 rather than quietly ignored, so pass what the buyer
|
|
1620
|
+
* actually chose.
|
|
1621
|
+
*/
|
|
1622
|
+
status?: string;
|
|
1623
|
+
/** Partial invoice-id search. The producer trims it to 64 characters. */
|
|
1624
|
+
search?: string;
|
|
1625
|
+
}
|
|
1626
|
+
interface CustomerTicketPayload {
|
|
1627
|
+
title?: string;
|
|
1628
|
+
message: string;
|
|
1629
|
+
invoice_id?: string;
|
|
1630
|
+
}
|
|
1631
|
+
interface CustomerProfilePatch {
|
|
1632
|
+
name: string;
|
|
1633
|
+
}
|
|
1634
|
+
interface CustomerSubscriptionCancelOptions {
|
|
1635
|
+
cancel_at_period_end?: boolean;
|
|
1636
|
+
reason?: string | null;
|
|
1637
|
+
}
|
|
1638
|
+
interface CustomerEmailPreferencesPatch {
|
|
1639
|
+
global_unsubscribed?: boolean;
|
|
1640
|
+
list_subscriptions?: Array<{
|
|
1641
|
+
list_id: string;
|
|
1642
|
+
subscribed: boolean;
|
|
1643
|
+
}>;
|
|
1644
|
+
}
|
|
1645
|
+
/** One basket line for a QUOTE. `variant_id` is omitted, never null, when a product has no variants. */
|
|
1646
|
+
interface ResellerOrderItem {
|
|
1647
|
+
product_id: string;
|
|
1648
|
+
variant_id?: string;
|
|
1649
|
+
quantity: number;
|
|
1650
|
+
}
|
|
1651
|
+
interface ResellerCatalogQuery {
|
|
1652
|
+
search?: string;
|
|
1653
|
+
page?: number;
|
|
1654
|
+
per_page?: number;
|
|
1655
|
+
}
|
|
1656
|
+
interface ResellerOrdersQuery {
|
|
1657
|
+
page?: number;
|
|
1658
|
+
per_page?: number;
|
|
1659
|
+
}
|
|
1660
|
+
declare function requestOtp(email: string): Promise<SDKResponse<unknown>>;
|
|
1661
|
+
/**
|
|
1662
|
+
* The wire field is `otp`, not `code` — `CustomerOtpVerifyBodySchema` in
|
|
1663
|
+
* `apps/backend-elysia/src/routes/v1/customer/schemas.ts` requires it under that
|
|
1664
|
+
* name, and a `code` body is rejected with 400.
|
|
1665
|
+
*/
|
|
1666
|
+
declare function verifyOtp(email: string, otp: string): Promise<SDKResponse<unknown>>;
|
|
1667
|
+
declare function logout(): Promise<SDKResponse<unknown>>;
|
|
1668
|
+
declare function me(): Promise<SDKResponse<unknown>>;
|
|
1669
|
+
declare function dashboard(): Promise<SDKResponse<CustomerPortalDashboardWire>>;
|
|
1670
|
+
/**
|
|
1671
|
+
* Paginated order history.
|
|
1672
|
+
*
|
|
1673
|
+
* Page-based, matching the producer: `invoices.routes.ts` reads `page` and
|
|
1674
|
+
* `limit` and answers with a `has_more` envelope. A cursor parameter would be
|
|
1675
|
+
* ignored upstream and silently return page one forever.
|
|
1676
|
+
*
|
|
1677
|
+
* `status` and `search` are filtered SERVER-side, which is why they belong
|
|
1678
|
+
* here rather than in the caller: `/invoices` is paged, and a filter applied
|
|
1679
|
+
* to the page in hand would filter only the rows that page happens to hold.
|
|
1680
|
+
* The producer spells the search `q`.
|
|
1681
|
+
*/
|
|
1682
|
+
declare function orders(options?: CustomerOrdersQuery): Promise<SDKResponse<CustomerPortalPaginatedInvoicesWire>>;
|
|
1683
|
+
declare function order(id: string): Promise<SDKResponse<CustomerPortalInvoiceDetailWire>>;
|
|
1684
|
+
declare function loyalty(): Promise<SDKResponse<CustomerLoyaltyWire>>;
|
|
1685
|
+
declare function redeemLoyaltyPoints(input: {
|
|
1686
|
+
points: number;
|
|
1687
|
+
idempotencyKey: string;
|
|
1688
|
+
}): Promise<SDKResponse<CustomerLoyaltyRedeemWire>>;
|
|
1689
|
+
/**
|
|
1690
|
+
* The warranties this buyer holds — all of them, or the ones on one order.
|
|
1691
|
+
*
|
|
1692
|
+
* `invoiceUniqid` becomes the producer's own `invoice=` filter, which matches
|
|
1693
|
+
* `invoices.uniqid`: the id an order carries everywhere a storefront shows one.
|
|
1694
|
+
* So an order detail asks for its own cover rather than reading every warranty
|
|
1695
|
+
* the buyer owns and discarding most of them.
|
|
1696
|
+
*/
|
|
1697
|
+
declare function warranties(invoiceUniqid?: string): Promise<SDKResponse<CustomerWarrantyListWire>>;
|
|
1698
|
+
declare function claimWarranty(uniqid: string, message?: string): Promise<SDKResponse<CustomerWarrantyClaimWire>>;
|
|
1699
|
+
declare function resetLicenseHwid(uniqid: string): Promise<SDKResponse<unknown>>;
|
|
1700
|
+
declare function subscriptionBillingHistory(uniqid: string): Promise<SDKResponse<unknown>>;
|
|
1701
|
+
declare function cancelSubscription(uniqid: string, options?: CustomerSubscriptionCancelOptions): Promise<SDKResponse<unknown>>;
|
|
1702
|
+
declare function pauseSubscription(uniqid: string): Promise<SDKResponse<unknown>>;
|
|
1703
|
+
declare function resumeSubscription(uniqid: string): Promise<SDKResponse<unknown>>;
|
|
1704
|
+
declare function favorites(): Promise<SDKResponse<unknown>>;
|
|
1705
|
+
/** Idempotent upstream, and a PUT — `customer-favorites.ts` has no POST route. */
|
|
1706
|
+
declare function addFavorite(productUniqid: string): Promise<SDKResponse<unknown>>;
|
|
1707
|
+
declare function removeFavorite(productUniqid: string): Promise<SDKResponse<unknown>>;
|
|
1708
|
+
declare function affiliate(): Promise<SDKResponse<unknown>>;
|
|
1709
|
+
declare function affiliateStats(days?: number): Promise<SDKResponse<unknown>>;
|
|
1710
|
+
declare function createTicket(payload: CustomerTicketPayload): Promise<SDKResponse<unknown>>;
|
|
1711
|
+
declare function ticket(uniqid: string): Promise<SDKResponse<unknown>>;
|
|
1712
|
+
declare function replyToTicket(uniqid: string, message: string): Promise<SDKResponse<unknown>>;
|
|
1713
|
+
/**
|
|
1714
|
+
* The producer is `POST /v1/customer/profile`, not PATCH.
|
|
1715
|
+
*
|
|
1716
|
+
* There is no `GET /profile` to pair with it — the buyer's own record comes from
|
|
1717
|
+
* `/me` and `/dashboard`.
|
|
1718
|
+
*/
|
|
1719
|
+
declare function updateProfile(patch: CustomerProfilePatch): Promise<SDKResponse<unknown>>;
|
|
1720
|
+
declare function updateAvatar(file: File): Promise<SDKResponse<unknown>>;
|
|
1721
|
+
declare function removeAvatar(): Promise<SDKResponse<unknown>>;
|
|
1722
|
+
declare function emailPreferences(): Promise<SDKResponse<unknown>>;
|
|
1723
|
+
declare function updateEmailPreferences(patch: CustomerEmailPreferencesPatch): Promise<SDKResponse<unknown>>;
|
|
1724
|
+
declare function sessions(): Promise<SDKResponse<unknown>>;
|
|
1725
|
+
declare function revokeSession(id: string): Promise<SDKResponse<unknown>>;
|
|
1726
|
+
/**
|
|
1727
|
+
* Ends every session except this one.
|
|
1728
|
+
*
|
|
1729
|
+
* `others=true` is required, not decorative: `RevokeOtherSessionsQuerySchema`
|
|
1730
|
+
* declares it as a literal, so the route answers 400 without it. The selector
|
|
1731
|
+
* is explicit on purpose — "revoke sessions" with no qualifier is one typo away
|
|
1732
|
+
* from signing the buyer out of the device they are holding.
|
|
1733
|
+
*/
|
|
1734
|
+
declare function revokeAllSessions(): Promise<SDKResponse<unknown>>;
|
|
1735
|
+
/** The program's state and this buyer's place in it. Null reseller means not enrolled. */
|
|
1736
|
+
declare function reseller(): Promise<SDKResponse<unknown>>;
|
|
1737
|
+
/** `APPLICATION` mode. The note is optional unless the shop requires one. */
|
|
1738
|
+
declare function applyForReseller(note?: string): Promise<SDKResponse<unknown>>;
|
|
1739
|
+
/** `OPEN` mode: no application, the buyer is a reseller when they say so. */
|
|
1740
|
+
declare function enrollAsReseller(): Promise<SDKResponse<unknown>>;
|
|
1741
|
+
/** `MANUAL` mode: the merchant invited this buyer and gave them a token. */
|
|
1742
|
+
declare function acceptResellerInvite(token: string): Promise<SDKResponse<unknown>>;
|
|
1743
|
+
/** The buyer's own tier prices. Page-based, like the order history. */
|
|
1744
|
+
declare function resellerCatalog(options?: ResellerCatalogQuery): Promise<SDKResponse<unknown>>;
|
|
1745
|
+
/**
|
|
1746
|
+
* Prices a basket and writes nothing.
|
|
1747
|
+
*
|
|
1748
|
+
* The volume discount cannot be derived from the catalog — it only reports that
|
|
1749
|
+
* one exists — so a storefront that adds up unit prices itself shows a total
|
|
1750
|
+
* the shop will not charge. This is the number to display.
|
|
1751
|
+
*/
|
|
1752
|
+
declare function quoteResellerOrder(items: ResellerOrderItem[]): Promise<SDKResponse<unknown>>;
|
|
1753
|
+
declare function resellerOrders(options?: ResellerOrdersQuery): Promise<SDKResponse<unknown>>;
|
|
1754
|
+
/** One order with its lines, delivery state and delivered serials. */
|
|
1755
|
+
declare function resellerOrder(uniqid: string): Promise<SDKResponse<unknown>>;
|
|
1756
|
+
/** The prepaid balance the wholesale orders are paid from. */
|
|
1757
|
+
declare function resellerWallet(): Promise<SDKResponse<unknown>>;
|
|
1758
|
+
declare function resellerApiKeys(): Promise<SDKResponse<unknown>>;
|
|
1759
|
+
|
|
1760
|
+
/**
|
|
1761
|
+
* Invoices Module
|
|
1762
|
+
*
|
|
1763
|
+
* Invoice status checking after payment.
|
|
1764
|
+
*/
|
|
1765
|
+
|
|
1766
|
+
declare function getInvoice(invoiceId: string): Promise<SDKResponse<Invoice>>;
|
|
1767
|
+
declare function getInvoiceStatus(invoiceId: string): Promise<SDKResponse<{
|
|
1768
|
+
status: string;
|
|
1769
|
+
}>>;
|
|
1770
|
+
|
|
1771
|
+
/**
|
|
1772
|
+
* Pages Module
|
|
1773
|
+
*
|
|
1774
|
+
* API methods for public pages.
|
|
1775
|
+
*/
|
|
1776
|
+
|
|
1777
|
+
/**
|
|
1778
|
+
* Get all public pages for the store
|
|
1779
|
+
*/
|
|
1780
|
+
declare function getPages(): Promise<SDKResponse<Page[]>>;
|
|
1781
|
+
/**
|
|
1782
|
+
* Get a public page by slug
|
|
1783
|
+
*/
|
|
1784
|
+
declare function getPage(slug: string): Promise<SDKResponse<Page>>;
|
|
1785
|
+
|
|
1786
|
+
/**
|
|
1787
|
+
* Navigation Module
|
|
1788
|
+
*
|
|
1789
|
+
* API methods for menus and navigation.
|
|
1790
|
+
*/
|
|
1791
|
+
|
|
1792
|
+
/**
|
|
1793
|
+
* Get all menus for the store
|
|
1794
|
+
*/
|
|
1795
|
+
declare function getMenus(): Promise<SDKResponse<Menu[]>>;
|
|
1796
|
+
/**
|
|
1797
|
+
* Get a menu by its exact title.
|
|
1798
|
+
*/
|
|
1799
|
+
declare function getMenuByTitle(title: string): Promise<SDKResponse<Menu>>;
|
|
1800
|
+
/**
|
|
1801
|
+
* Get a menu by its exact title.
|
|
1802
|
+
*/
|
|
1803
|
+
declare function getMenu(title: string): Promise<SDKResponse<Menu>>;
|
|
1804
|
+
/**
|
|
1805
|
+
* Get a menu by canonical slot. The backend resolves legacy menu titles too.
|
|
1806
|
+
*/
|
|
1807
|
+
declare function getMenuBySlot(slot: NavigationMenuSlot): Promise<SDKResponse<Menu>>;
|
|
1808
|
+
declare function getMenuSlotTitles(slot: NavigationMenuSlot): string[];
|
|
1809
|
+
|
|
1810
|
+
declare function trackPageView(cartValue?: number, itemCount?: number): Promise<void>;
|
|
1811
|
+
|
|
1812
|
+
/**
|
|
1813
|
+
* Anonymized recent-sales feed for storefront social proof (product title +
|
|
1814
|
+
* relative age only — never buyer identity). Empty items when the shop has no
|
|
1815
|
+
* recent completed orders.
|
|
1816
|
+
*/
|
|
1817
|
+
declare function getStorefrontRecentSales(): Promise<SDKResponse<StorefrontRecentSales>>;
|
|
1818
|
+
declare function getStorefrontOnlineUsers(): Promise<SDKResponse<StorefrontOnlineUsers>>;
|
|
1819
|
+
declare function touchStorefrontPresence(): Promise<SDKResponse<{
|
|
1820
|
+
pong: string;
|
|
1821
|
+
}>>;
|
|
1822
|
+
|
|
1823
|
+
/**
|
|
1824
|
+
* Formatting Utilities
|
|
1825
|
+
*/
|
|
1826
|
+
declare function createFormatter(currency?: string, locale?: string): Intl.NumberFormat;
|
|
1827
|
+
declare function formatPrice(amount: number | string, currency?: string, locale?: string): string;
|
|
1828
|
+
|
|
1829
|
+
interface CacheStats {
|
|
1830
|
+
hits: number;
|
|
1831
|
+
misses: number;
|
|
1832
|
+
pendingRequests: number;
|
|
1833
|
+
entries: number;
|
|
1834
|
+
}
|
|
1835
|
+
declare function getCacheStats(): CacheStats;
|
|
1836
|
+
declare function clearCache(): void;
|
|
1837
|
+
declare function invalidateCache(prefixOrKey: string): void;
|
|
1838
|
+
|
|
1839
|
+
/**
|
|
1840
|
+
* Shoppex Storefront SDK
|
|
1841
|
+
*
|
|
1842
|
+
* Usage:
|
|
1843
|
+
* ```html
|
|
1844
|
+
* <script src="https://cdn.shoppex.io/sdk/v1.0/shoppex.umd.js"></script>
|
|
1845
|
+
* <script>
|
|
1846
|
+
* shoppex.init('my-store');
|
|
1847
|
+
*
|
|
1848
|
+
* shoppex.getStore().then(store => console.log(store));
|
|
1849
|
+
* shoppex.addToCart('product-id', 'variant-id', 2);
|
|
1850
|
+
* shoppex.checkout();
|
|
1851
|
+
* </script>
|
|
1852
|
+
* ```
|
|
1853
|
+
*/
|
|
1854
|
+
|
|
47
1855
|
/**
|
|
48
1856
|
* Initialize the SDK with a store slug
|
|
49
1857
|
*/
|
|
@@ -54,7 +1862,7 @@ declare function init(options: ShoppexInitOptions & {
|
|
|
54
1862
|
/**
|
|
55
1863
|
* Shoppex SDK instance
|
|
56
1864
|
*/
|
|
57
|
-
|
|
1865
|
+
declare const shoppex: {
|
|
58
1866
|
init: typeof init;
|
|
59
1867
|
isInitialized: typeof isInitialized;
|
|
60
1868
|
getConfig: typeof getConfig;
|
|
@@ -96,6 +1904,7 @@ export declare const shoppex: {
|
|
|
96
1904
|
getAffiliateCode: typeof getAffiliateCode;
|
|
97
1905
|
setAffiliateCode: typeof setAffiliateCode;
|
|
98
1906
|
clearAffiliateCode: typeof clearAffiliateCode;
|
|
1907
|
+
trackAffiliateEvent: typeof trackAffiliateEvent;
|
|
99
1908
|
validateCoupon: typeof validateCoupon;
|
|
100
1909
|
getShopReviews: typeof getShopReviews;
|
|
101
1910
|
getShopReviewsPage: typeof getShopReviewsPage;
|
|
@@ -119,6 +1928,7 @@ export declare const shoppex: {
|
|
|
119
1928
|
addFavorite: typeof addFavorite;
|
|
120
1929
|
removeFavorite: typeof removeFavorite;
|
|
121
1930
|
affiliate: typeof affiliate;
|
|
1931
|
+
affiliateStats: typeof affiliateStats;
|
|
122
1932
|
createTicket: typeof createTicket;
|
|
123
1933
|
ticket: typeof ticket;
|
|
124
1934
|
replyToTicket: typeof replyToTicket;
|
|
@@ -165,5 +1975,5 @@ export declare const shoppex: {
|
|
|
165
1975
|
resolveDefaults: typeof resolveDefaults;
|
|
166
1976
|
mergeSettings: typeof mergeSettings;
|
|
167
1977
|
};
|
|
168
|
-
|
|
169
|
-
|
|
1978
|
+
|
|
1979
|
+
export { type AffiliateValidation, type ApiChallenge, ApiError, type ApiResponse, type BlockDefinition, type BlockInstance, type BuyerRewardActivityItem, type BuyerRewards, type CartAddOptions, type CartAddon, type CartAppliedDiscount, type CartBasketMergeLine, type CartCodeSource, CartError, type CartItem, type CartItemUpdate, type CartLineIdentityInput, type CartMetadata, type CartPayload, type CartQuote, type CartQuoteLine, type CartStats, type Category, CheckoutCreateError, type CheckoutOptions, type CheckoutResult, type CouponValidation, type CouponValidationOptions, type CursorPagination, type CustomFieldDefinition, type CustomerEmailPreferencesPatch, type CustomerProfilePatch, type CustomerSubscriptionCancelOptions, type CustomerTicketPayload, type EligibleBundleDeal, type EligibleBundleDealProduct, type EligibleCartDeal, type Feedback, type Invoice, type InvoiceProduct, type Menu, type MenuItem, NetworkError, NotInitializedError, type Page, type PageLayout, type PriceVariant, type Product, type ProductAddon, type ProductBundle, type ProductBundleProduct, type ProductCategory, type ProductDescriptionTab, type ProductFaq, type ProductFeedback, type ProductGroup, type ProductImage, type ProductVariant, type PublishedBuilderSettings, type PublishedThemeSettingsPayload, type ResellerCatalogQuery, type ResellerOrderItem, type ResellerOrdersQuery, type ResolvedThemeSettings, type RewardActionType, type RewardGrantStatus, type RewardProgramSummary, type RewardProgramTeaserItem, type RewardReason, type SDKResponse, type SearchOptions, type SectionDefinition, type SettingField, type Shop, type ShopFeedback, type ShopReviewsPage, type ShopReviewsSummary, type ShopTheme, type ShoppexConfig, ShoppexError, type ShoppexInitOptions, type StorefrontAddon, type StorefrontAddonBootstrap, type StorefrontAnnouncementBarAddon, type StorefrontCatalogSearchItem, type StorefrontContactTicketInput, type StorefrontContactTicketResult, type StorefrontCountdownBarAddon, type StorefrontCouponPopupModalAddon, type StorefrontCustomField, type StorefrontData, type StorefrontItem, type StorefrontOnlineUsers, type StorefrontPromoInfoCardAddon, type StorefrontRecentPurchasePopupAddon, type StorefrontRecentSaleEntry, type StorefrontRecentSales, type StorefrontSearchFilterOptions, type StorefrontSocialLinks, type Subscription, type SubscriptionFlags, type SubscriptionInterval, type ThemeBlockManifest, type ThemeConfig, type TrustedChecks, ValidationError, acceptResellerInvite, addFavorite, affiliate, affiliateStats, applyForReseller, buildStorefrontContactMessage, buildStorefrontCustomFieldPayload, buildStorefrontProductLookup, cancelSubscription, claimWarranty, collectProductSearchHaystack, computeCartLineId, createTicket, dashboard, shoppex as default, emailPreferences, enrollAsReseller, ensureCartLineId, favorites, fetchPublishedBuilderSettings, fetchPublishedThemeSettings, filterProductsBySearchQuery, getMenu, getMenuBySlot, getMenuByTitle, getMenuSlotTitles, getMenus, getMergedStorefrontProducts, getShopReviewsPage, getStorefrontGroupProducts, getStorefrontOnlineUsers, getStorefrontRecentSales, groupMatchesSearchQuery, isProductInStock, isProductOutOfStock, isStorefrontCheckboxCustomFieldValueChecked, isVariantOutOfStock, logout, loyalty, me, mergeSettings, normalizeSearchQuery, normalizeStorefrontCustomFields, order, orders, pauseSubscription, productMatchesSearchQuery, quoteResellerOrder, redeemLoyaltyPoints, removeAvatar, removeFavorite, replyToTicket, requestOtp, reseller, resellerApiKeys, resellerCatalog, resellerOrder, resellerOrders, resellerWallet, resetLicenseHwid, resolveDefaults, resolveDisplayStock, resolveStorefrontApiBaseUrl, resolveStorefrontSocialLinks, resolveVariantStockValue, resumeSubscription, revokeAllSessions, revokeSession, searchMergedStorefrontCatalog, searchMergedStorefrontCatalogItems, sessions, shoppex, stripHtmlFromText, submitStorefrontContactTicket, subscriptionBillingHistory, ticket, touchStorefrontPresence, trackPageView, updateAvatar, updateEmailPreferences, updateProfile, validateStorefrontCustomFieldValue, verifyOtp, warranties };
|