@behio/storefront-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +999 -0
- package/dist/chunk-26WZOAWP.js +609 -0
- package/dist/chunk-3OJEEMLI.mjs +609 -0
- package/dist/index.d.mts +566 -0
- package/dist/index.d.ts +566 -0
- package/dist/index.js +20 -0
- package/dist/index.mjs +20 -0
- package/dist/react.d.mts +166 -0
- package/dist/react.d.ts +166 -0
- package/dist/react.js +813 -0
- package/dist/react.mjs +813 -0
- package/package.json +61 -0
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var ProductSort = {
|
|
3
|
+
PRICE_ASC: "price_asc",
|
|
4
|
+
PRICE_DESC: "price_desc",
|
|
5
|
+
NAME_ASC: "name_asc",
|
|
6
|
+
NAME_DESC: "name_desc",
|
|
7
|
+
NEWEST: "newest",
|
|
8
|
+
FEATURED: "featured"
|
|
9
|
+
};
|
|
10
|
+
var OrderStatuses = {
|
|
11
|
+
PENDING: "PENDING",
|
|
12
|
+
CONFIRMED: "CONFIRMED",
|
|
13
|
+
PROCESSING: "PROCESSING",
|
|
14
|
+
SHIPPED: "SHIPPED",
|
|
15
|
+
DELIVERED: "DELIVERED",
|
|
16
|
+
CANCELLED: "CANCELLED",
|
|
17
|
+
REFUNDED: "REFUNDED"
|
|
18
|
+
};
|
|
19
|
+
var PaymentStatuses = {
|
|
20
|
+
UNPAID: "UNPAID",
|
|
21
|
+
PAID: "PAID",
|
|
22
|
+
PARTIALLY_REFUNDED: "PARTIALLY_REFUNDED",
|
|
23
|
+
REFUNDED: "REFUNDED"
|
|
24
|
+
};
|
|
25
|
+
var FulfillmentStatuses = {
|
|
26
|
+
UNFULFILLED: "UNFULFILLED",
|
|
27
|
+
PARTIALLY_FULFILLED: "PARTIALLY_FULFILLED",
|
|
28
|
+
FULFILLED: "FULFILLED"
|
|
29
|
+
};
|
|
30
|
+
var AddressTypes = {
|
|
31
|
+
SHIPPING: "SHIPPING",
|
|
32
|
+
BILLING: "BILLING"
|
|
33
|
+
};
|
|
34
|
+
var BehioApiError = class _BehioApiError extends Error {
|
|
35
|
+
constructor(status, body, message) {
|
|
36
|
+
super(message || `API Error ${status}`);
|
|
37
|
+
this.name = "BehioApiError";
|
|
38
|
+
this.status = status;
|
|
39
|
+
this.body = body;
|
|
40
|
+
this.code = _BehioApiError.resolveCode(status, body);
|
|
41
|
+
this.isRetryable = status >= 500 || status === 429;
|
|
42
|
+
}
|
|
43
|
+
static resolveCode(status, body) {
|
|
44
|
+
if (status === 401) return "UNAUTHORIZED";
|
|
45
|
+
if (status === 403) return "FORBIDDEN";
|
|
46
|
+
if (status === 404) return "NOT_FOUND";
|
|
47
|
+
if (status === 409) return "EMAIL_ALREADY_EXISTS";
|
|
48
|
+
if (status === 429) return "RATE_LIMITED";
|
|
49
|
+
if (status >= 500) return "INTERNAL_ERROR";
|
|
50
|
+
const msg = (body?.message || "").toLowerCase();
|
|
51
|
+
if (msg.includes("invalid") && msg.includes("password")) return "INVALID_CREDENTIALS";
|
|
52
|
+
if (msg.includes("invalid") && msg.includes("email")) return "INVALID_CREDENTIALS";
|
|
53
|
+
if (msg.includes("cart") && msg.includes("empty")) return "CART_EMPTY";
|
|
54
|
+
if (msg.includes("product") && msg.includes("not found")) return "PRODUCT_NOT_FOUND";
|
|
55
|
+
if (msg.includes("discount") && msg.includes("expired")) return "DISCOUNT_EXPIRED";
|
|
56
|
+
if (msg.includes("discount") && msg.includes("invalid")) return "INVALID_DISCOUNT";
|
|
57
|
+
if (msg.includes("token") && msg.includes("expired")) return "TOKEN_EXPIRED";
|
|
58
|
+
if (msg.includes("cancel")) return "ORDER_NOT_CANCELLABLE";
|
|
59
|
+
if (status === 400) return "VALIDATION_ERROR";
|
|
60
|
+
return "UNKNOWN";
|
|
61
|
+
}
|
|
62
|
+
/** Check if this is a specific error type */
|
|
63
|
+
is(code) {
|
|
64
|
+
return this.code === code;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var BehioNetworkError = class extends Error {
|
|
68
|
+
constructor(message, isTimeout = false) {
|
|
69
|
+
super(message);
|
|
70
|
+
this.isRetryable = true;
|
|
71
|
+
this.name = "BehioNetworkError";
|
|
72
|
+
this.code = isTimeout ? "TIMEOUT" : "NETWORK_ERROR";
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// src/client.ts
|
|
77
|
+
var BehioStorefront = class {
|
|
78
|
+
constructor(config) {
|
|
79
|
+
// Token refresh lock
|
|
80
|
+
this.isRefreshing = false;
|
|
81
|
+
this.refreshPromise = null;
|
|
82
|
+
// Event emitter
|
|
83
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
84
|
+
// Interceptors
|
|
85
|
+
this.requestInterceptors = [];
|
|
86
|
+
this.responseInterceptors = [];
|
|
87
|
+
// Rate limit tracking
|
|
88
|
+
this.rateLimitRemaining = null;
|
|
89
|
+
this.rateLimitReset = null;
|
|
90
|
+
this.baseUrl = (config.baseUrl || "https://api.behio.com").replace(/\/$/, "");
|
|
91
|
+
this.apiKey = config.apiKey;
|
|
92
|
+
this.defaultLocale = config.locale;
|
|
93
|
+
this.defaultCurrency = config.currency;
|
|
94
|
+
this.fetchFn = config.fetch || globalThis.fetch;
|
|
95
|
+
this.timeout = config.timeout ?? 3e4;
|
|
96
|
+
this.retries = config.retries ?? 1;
|
|
97
|
+
this.retryDelay = config.retryDelay ?? 1e3;
|
|
98
|
+
this.catalog = new CatalogModule(this);
|
|
99
|
+
this.auth = new AuthModule(this);
|
|
100
|
+
this.cart = new CartModule(this);
|
|
101
|
+
this.checkout = new CheckoutModule(this);
|
|
102
|
+
this.orders = new OrdersModule(this);
|
|
103
|
+
this.customer = new CustomerModule(this);
|
|
104
|
+
this.pages = new PagesModule(this);
|
|
105
|
+
}
|
|
106
|
+
// --- Public methods ---
|
|
107
|
+
/** Get basic shop info */
|
|
108
|
+
async getShopInfo() {
|
|
109
|
+
return this.request("GET", "/shop");
|
|
110
|
+
}
|
|
111
|
+
/** Set auth tokens (e.g. from localStorage) */
|
|
112
|
+
setTokens(tokens) {
|
|
113
|
+
this.accessToken = tokens.accessToken;
|
|
114
|
+
this.refreshToken = tokens.refreshToken;
|
|
115
|
+
}
|
|
116
|
+
/** Clear auth tokens */
|
|
117
|
+
clearTokens() {
|
|
118
|
+
this.accessToken = void 0;
|
|
119
|
+
this.refreshToken = void 0;
|
|
120
|
+
}
|
|
121
|
+
/** Get current access token */
|
|
122
|
+
getAccessToken() {
|
|
123
|
+
return this.accessToken;
|
|
124
|
+
}
|
|
125
|
+
/** Get current refresh token */
|
|
126
|
+
getRefreshToken() {
|
|
127
|
+
return this.refreshToken;
|
|
128
|
+
}
|
|
129
|
+
/** Set cart session token (e.g. from cookie) */
|
|
130
|
+
setCartSession(token) {
|
|
131
|
+
this.cartSession = token;
|
|
132
|
+
}
|
|
133
|
+
/** Get cart session token */
|
|
134
|
+
getCartSession() {
|
|
135
|
+
return this.cartSession;
|
|
136
|
+
}
|
|
137
|
+
/** Clear cart session */
|
|
138
|
+
clearCartSession() {
|
|
139
|
+
this.cartSession = void 0;
|
|
140
|
+
}
|
|
141
|
+
// --- Event emitter ---
|
|
142
|
+
/** Subscribe to SDK events. Returns an unsubscribe function. */
|
|
143
|
+
on(event, handler) {
|
|
144
|
+
if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
145
|
+
this.listeners.get(event).add(handler);
|
|
146
|
+
return () => {
|
|
147
|
+
this.listeners.get(event)?.delete(handler);
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/** @internal Emit an event (fire-and-forget, handler errors are swallowed) */
|
|
151
|
+
emit(event, data) {
|
|
152
|
+
this.listeners.get(event)?.forEach((fn) => {
|
|
153
|
+
try {
|
|
154
|
+
fn(data);
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
// --- Interceptors ---
|
|
160
|
+
/** Add a request interceptor. Returns an unsubscribe function. */
|
|
161
|
+
addRequestInterceptor(fn) {
|
|
162
|
+
this.requestInterceptors.push(fn);
|
|
163
|
+
return () => {
|
|
164
|
+
this.requestInterceptors = this.requestInterceptors.filter((f) => f !== fn);
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/** Add a response interceptor. Returns an unsubscribe function. */
|
|
168
|
+
addResponseInterceptor(fn) {
|
|
169
|
+
this.responseInterceptors.push(fn);
|
|
170
|
+
return () => {
|
|
171
|
+
this.responseInterceptors = this.responseInterceptors.filter((f) => f !== fn);
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
// --- Rate limit ---
|
|
175
|
+
/** Get current rate limit info from latest response headers */
|
|
176
|
+
getRateLimitInfo() {
|
|
177
|
+
return { remaining: this.rateLimitRemaining, reset: this.rateLimitReset };
|
|
178
|
+
}
|
|
179
|
+
// --- Token refresh ---
|
|
180
|
+
async handleTokenRefresh() {
|
|
181
|
+
if (this.isRefreshing) {
|
|
182
|
+
if (this.refreshPromise) await this.refreshPromise;
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
this.isRefreshing = true;
|
|
186
|
+
this.refreshPromise = (async () => {
|
|
187
|
+
try {
|
|
188
|
+
await this.auth.refresh();
|
|
189
|
+
this.emit("auth:token-refresh");
|
|
190
|
+
} catch {
|
|
191
|
+
this.clearTokens();
|
|
192
|
+
this.emit("auth:token-refresh-failed");
|
|
193
|
+
throw new BehioApiError(401, null, "Token refresh failed");
|
|
194
|
+
} finally {
|
|
195
|
+
this.isRefreshing = false;
|
|
196
|
+
this.refreshPromise = null;
|
|
197
|
+
}
|
|
198
|
+
})();
|
|
199
|
+
return this.refreshPromise;
|
|
200
|
+
}
|
|
201
|
+
// --- Internal fetch ---
|
|
202
|
+
/** @internal */
|
|
203
|
+
async request(method, path, options) {
|
|
204
|
+
const url = new URL(`${this.baseUrl}/storefront/v1${path}`);
|
|
205
|
+
if (options?.query) {
|
|
206
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
207
|
+
if (value !== void 0 && value !== null && value !== "") {
|
|
208
|
+
url.searchParams.set(key, String(value));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (this.defaultLocale && !url.searchParams.has("locale")) {
|
|
213
|
+
url.searchParams.set("locale", this.defaultLocale);
|
|
214
|
+
}
|
|
215
|
+
if (this.defaultCurrency && !url.searchParams.has("currency")) {
|
|
216
|
+
url.searchParams.set("currency", this.defaultCurrency);
|
|
217
|
+
}
|
|
218
|
+
const headers = {
|
|
219
|
+
"X-Api-Key": this.apiKey,
|
|
220
|
+
"Content-Type": "application/json"
|
|
221
|
+
};
|
|
222
|
+
if (this.accessToken && options?.auth !== false) {
|
|
223
|
+
headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
224
|
+
}
|
|
225
|
+
if (this.cartSession) {
|
|
226
|
+
headers["X-Cart-Session"] = this.cartSession;
|
|
227
|
+
}
|
|
228
|
+
const bodyStr = options?.body ? JSON.stringify(options.body) : void 0;
|
|
229
|
+
let interceptedConfig = {
|
|
230
|
+
url: url.toString(),
|
|
231
|
+
method,
|
|
232
|
+
headers,
|
|
233
|
+
body: bodyStr
|
|
234
|
+
};
|
|
235
|
+
for (const interceptor of this.requestInterceptors) {
|
|
236
|
+
interceptedConfig = await interceptor(interceptedConfig);
|
|
237
|
+
}
|
|
238
|
+
this.emit("request", { method, path });
|
|
239
|
+
for (let attempt = 0; attempt <= this.retries; attempt++) {
|
|
240
|
+
const controller = new AbortController();
|
|
241
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
242
|
+
if (options?.signal) {
|
|
243
|
+
if (options.signal.aborted) {
|
|
244
|
+
clearTimeout(timeoutId);
|
|
245
|
+
throw new BehioNetworkError("Request aborted", false);
|
|
246
|
+
}
|
|
247
|
+
options.signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
248
|
+
}
|
|
249
|
+
let res;
|
|
250
|
+
try {
|
|
251
|
+
res = await this.fetchFn(interceptedConfig.url, {
|
|
252
|
+
method: interceptedConfig.method,
|
|
253
|
+
headers: interceptedConfig.headers,
|
|
254
|
+
body: interceptedConfig.body,
|
|
255
|
+
signal: controller.signal
|
|
256
|
+
});
|
|
257
|
+
} catch (err) {
|
|
258
|
+
clearTimeout(timeoutId);
|
|
259
|
+
const isAbort = err instanceof DOMException && err.name === "AbortError";
|
|
260
|
+
const networkErr = new BehioNetworkError(
|
|
261
|
+
isAbort ? "Request timed out" : err.message || "Network error",
|
|
262
|
+
isAbort
|
|
263
|
+
);
|
|
264
|
+
this.emit("error", networkErr);
|
|
265
|
+
if (attempt < this.retries) {
|
|
266
|
+
await new Promise((r) => setTimeout(r, this.retryDelay * (attempt + 1)));
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
throw networkErr;
|
|
270
|
+
} finally {
|
|
271
|
+
clearTimeout(timeoutId);
|
|
272
|
+
}
|
|
273
|
+
const remaining = res.headers.get("X-RateLimit-Remaining");
|
|
274
|
+
const reset = res.headers.get("X-RateLimit-Reset");
|
|
275
|
+
if (remaining) this.rateLimitRemaining = parseInt(remaining, 10);
|
|
276
|
+
if (reset) this.rateLimitReset = parseInt(reset, 10);
|
|
277
|
+
if (this.rateLimitRemaining !== null && this.rateLimitRemaining <= 5) {
|
|
278
|
+
this.emit("rate-limit-warning", { remaining: this.rateLimitRemaining, reset: this.rateLimitReset });
|
|
279
|
+
}
|
|
280
|
+
this.emit("response", { method, path, status: res.status });
|
|
281
|
+
if (!res.ok) {
|
|
282
|
+
const body = await res.json().catch(() => null);
|
|
283
|
+
const apiError = new BehioApiError(res.status, body, body?.message || `API Error ${res.status}`);
|
|
284
|
+
if (res.status === 401 && this.refreshToken && options?.auth !== false && !options?._isRetryAfterRefresh) {
|
|
285
|
+
try {
|
|
286
|
+
await this.handleTokenRefresh();
|
|
287
|
+
return this.request(method, path, { ...options, _isRetryAfterRefresh: true });
|
|
288
|
+
} catch {
|
|
289
|
+
this.emit("error", apiError);
|
|
290
|
+
throw apiError;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
this.emit("error", apiError);
|
|
294
|
+
if (attempt < this.retries && apiError.isRetryable) {
|
|
295
|
+
await new Promise((r) => setTimeout(r, this.retryDelay * (attempt + 1)));
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
throw apiError;
|
|
299
|
+
}
|
|
300
|
+
const responseData = res.status === 204 ? null : await res.json();
|
|
301
|
+
for (const interceptor of this.responseInterceptors) {
|
|
302
|
+
try {
|
|
303
|
+
await interceptor({ status: res.status, data: responseData, headers: res.headers });
|
|
304
|
+
} catch {
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return responseData;
|
|
308
|
+
}
|
|
309
|
+
throw new BehioNetworkError("Request failed after retries");
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
var CatalogModule = class {
|
|
313
|
+
constructor(client) {
|
|
314
|
+
this.client = client;
|
|
315
|
+
}
|
|
316
|
+
/** List products with filtering, pagination, search */
|
|
317
|
+
async getProducts(query) {
|
|
318
|
+
const q = {};
|
|
319
|
+
if (query) {
|
|
320
|
+
if (query.page) q.page = query.page;
|
|
321
|
+
if (query.limit) q.limit = query.limit;
|
|
322
|
+
if (query.category) q.category = query.category;
|
|
323
|
+
if (query.label) q.label = query.label;
|
|
324
|
+
if (query.priceMin) q.priceMin = query.priceMin;
|
|
325
|
+
if (query.priceMax) q.priceMax = query.priceMax;
|
|
326
|
+
if (query.currency) q.currency = query.currency;
|
|
327
|
+
if (query.locale) q.locale = query.locale;
|
|
328
|
+
if (query.sort) q.sort = query.sort;
|
|
329
|
+
if (query.inStock !== void 0) q.inStock = query.inStock;
|
|
330
|
+
if (query.search) q.search = query.search;
|
|
331
|
+
if (query.customFields) q.customFields = JSON.stringify(query.customFields);
|
|
332
|
+
}
|
|
333
|
+
return this.client.request("GET", "/catalog/products", { query: q });
|
|
334
|
+
}
|
|
335
|
+
/** Get product detail by slug */
|
|
336
|
+
async getProduct(slug, options) {
|
|
337
|
+
return this.client.request("GET", `/catalog/products/${slug}`, {
|
|
338
|
+
query: { locale: options?.locale, currency: options?.currency }
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
/** Get category tree */
|
|
342
|
+
async getCategories(locale) {
|
|
343
|
+
const res = await this.client.request("GET", "/catalog/categories", { query: { locale } });
|
|
344
|
+
return { categories: res.categories || res.items || [] };
|
|
345
|
+
}
|
|
346
|
+
/** Get category detail by slug */
|
|
347
|
+
async getCategory(slug, locale) {
|
|
348
|
+
return this.client.request("GET", `/catalog/categories/${slug}`, { query: { locale } });
|
|
349
|
+
}
|
|
350
|
+
/** Get products in a category */
|
|
351
|
+
async getCategoryProducts(slug, query) {
|
|
352
|
+
const q = {};
|
|
353
|
+
if (query) {
|
|
354
|
+
if (query.page) q.page = query.page;
|
|
355
|
+
if (query.limit) q.limit = query.limit;
|
|
356
|
+
if (query.sort) q.sort = query.sort;
|
|
357
|
+
if (query.locale) q.locale = query.locale;
|
|
358
|
+
if (query.currency) q.currency = query.currency;
|
|
359
|
+
}
|
|
360
|
+
return this.client.request("GET", `/catalog/categories/${slug}/products`, { query: q });
|
|
361
|
+
}
|
|
362
|
+
/** Get all labels */
|
|
363
|
+
async getLabels(locale) {
|
|
364
|
+
const res = await this.client.request("GET", "/catalog/labels", { query: { locale } });
|
|
365
|
+
return { labels: res.labels || res.items || [] };
|
|
366
|
+
}
|
|
367
|
+
/** Get featured products */
|
|
368
|
+
async getFeatured(options) {
|
|
369
|
+
return this.client.request("GET", "/catalog/featured", {
|
|
370
|
+
query: { locale: options?.locale, currency: options?.currency }
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
/** Get available filter fields for dynamic filter UI */
|
|
374
|
+
async getFilters() {
|
|
375
|
+
return this.client.request("GET", "/catalog/filters");
|
|
376
|
+
}
|
|
377
|
+
/** Search products */
|
|
378
|
+
async search(query, options) {
|
|
379
|
+
return this.getProducts({ search: query, ...options });
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
var AuthModule = class {
|
|
383
|
+
constructor(client) {
|
|
384
|
+
this.client = client;
|
|
385
|
+
}
|
|
386
|
+
/** Register a new customer */
|
|
387
|
+
async register(input) {
|
|
388
|
+
const tokens = await this.client.request("POST", "/auth/register", {
|
|
389
|
+
body: input,
|
|
390
|
+
auth: false
|
|
391
|
+
});
|
|
392
|
+
this.client.setTokens(tokens);
|
|
393
|
+
this.client.emit("auth:login", { email: input.email });
|
|
394
|
+
return tokens;
|
|
395
|
+
}
|
|
396
|
+
/** Login with email and password */
|
|
397
|
+
async login(input) {
|
|
398
|
+
const tokens = await this.client.request("POST", "/auth/login", {
|
|
399
|
+
body: input,
|
|
400
|
+
auth: false
|
|
401
|
+
});
|
|
402
|
+
this.client.setTokens(tokens);
|
|
403
|
+
this.client.emit("auth:login", { email: input.email });
|
|
404
|
+
return tokens;
|
|
405
|
+
}
|
|
406
|
+
/** Refresh access token using refresh token */
|
|
407
|
+
async refresh(refreshToken) {
|
|
408
|
+
const token = refreshToken || this.client.getRefreshToken();
|
|
409
|
+
if (!token) throw new Error("No refresh token available");
|
|
410
|
+
const tokens = await this.client.request("POST", "/auth/refresh", {
|
|
411
|
+
body: { refreshToken: token },
|
|
412
|
+
auth: false
|
|
413
|
+
});
|
|
414
|
+
this.client.setTokens(tokens);
|
|
415
|
+
return tokens;
|
|
416
|
+
}
|
|
417
|
+
/** Logout (invalidate refresh token) */
|
|
418
|
+
async logout(refreshToken) {
|
|
419
|
+
const token = refreshToken || this.client.getRefreshToken();
|
|
420
|
+
const result = await this.client.request("POST", "/auth/logout", {
|
|
421
|
+
body: { refreshToken: token }
|
|
422
|
+
});
|
|
423
|
+
this.client.clearTokens();
|
|
424
|
+
this.client.emit("auth:logout");
|
|
425
|
+
return result;
|
|
426
|
+
}
|
|
427
|
+
/** Request password reset email */
|
|
428
|
+
async forgotPassword(email) {
|
|
429
|
+
return this.client.request("POST", "/auth/forgot-password", {
|
|
430
|
+
body: { email },
|
|
431
|
+
auth: false
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
/** Reset password with token */
|
|
435
|
+
async resetPassword(token, newPassword) {
|
|
436
|
+
return this.client.request("POST", "/auth/reset-password", {
|
|
437
|
+
body: { token, newPassword },
|
|
438
|
+
auth: false
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
/** Verify email with token */
|
|
442
|
+
async verifyEmail(token) {
|
|
443
|
+
return this.client.request("POST", "/auth/verify-email", {
|
|
444
|
+
body: { token },
|
|
445
|
+
auth: false
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
/** Check if user is logged in (has access token) */
|
|
449
|
+
isLoggedIn() {
|
|
450
|
+
return !!this.client.getAccessToken();
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
var CartModule = class {
|
|
454
|
+
constructor(client) {
|
|
455
|
+
this.client = client;
|
|
456
|
+
}
|
|
457
|
+
/** Get current cart */
|
|
458
|
+
async get() {
|
|
459
|
+
return this.client.request("GET", "/cart");
|
|
460
|
+
}
|
|
461
|
+
/** Add item to cart */
|
|
462
|
+
async addItem(input) {
|
|
463
|
+
const result = await this.client.request("POST", "/cart/items", {
|
|
464
|
+
body: { whItemId: input.productId, quantity: input.quantity }
|
|
465
|
+
});
|
|
466
|
+
if (result.newSessionToken) {
|
|
467
|
+
this.client.setCartSession(result.newSessionToken);
|
|
468
|
+
}
|
|
469
|
+
this.client.emit("cart:updated", result);
|
|
470
|
+
return result;
|
|
471
|
+
}
|
|
472
|
+
/** Update item quantity */
|
|
473
|
+
async updateQuantity(itemId, quantity) {
|
|
474
|
+
const result = await this.client.request("PATCH", `/cart/items/${itemId}`, {
|
|
475
|
+
body: { quantity }
|
|
476
|
+
});
|
|
477
|
+
this.client.emit("cart:updated", result);
|
|
478
|
+
return result;
|
|
479
|
+
}
|
|
480
|
+
/** Remove item from cart */
|
|
481
|
+
async removeItem(itemId) {
|
|
482
|
+
const result = await this.client.request("DELETE", `/cart/items/${itemId}`);
|
|
483
|
+
this.client.emit("cart:updated", result);
|
|
484
|
+
return result;
|
|
485
|
+
}
|
|
486
|
+
/** Clear entire cart */
|
|
487
|
+
async clear() {
|
|
488
|
+
const result = await this.client.request("DELETE", "/cart");
|
|
489
|
+
this.client.emit("cart:cleared");
|
|
490
|
+
return result;
|
|
491
|
+
}
|
|
492
|
+
/** Merge anonymous cart into authenticated customer cart */
|
|
493
|
+
async merge() {
|
|
494
|
+
const result = await this.client.request("POST", "/cart/merge");
|
|
495
|
+
this.client.emit("cart:updated", result);
|
|
496
|
+
return result;
|
|
497
|
+
}
|
|
498
|
+
/** Apply discount code */
|
|
499
|
+
async applyDiscount(code) {
|
|
500
|
+
const result = await this.client.request("POST", "/cart/discount", {
|
|
501
|
+
body: { code }
|
|
502
|
+
});
|
|
503
|
+
this.client.emit("cart:updated", result);
|
|
504
|
+
return result;
|
|
505
|
+
}
|
|
506
|
+
/** Remove discount code */
|
|
507
|
+
async removeDiscount() {
|
|
508
|
+
const result = await this.client.request("DELETE", "/cart/discount");
|
|
509
|
+
this.client.emit("cart:updated", result);
|
|
510
|
+
return result;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
var CheckoutModule = class {
|
|
514
|
+
constructor(client) {
|
|
515
|
+
this.client = client;
|
|
516
|
+
}
|
|
517
|
+
/** Create order from cart */
|
|
518
|
+
async createOrder(input) {
|
|
519
|
+
const result = await this.client.request("POST", "/checkout", {
|
|
520
|
+
body: input
|
|
521
|
+
});
|
|
522
|
+
this.client.clearCartSession();
|
|
523
|
+
this.client.emit("order:created", result);
|
|
524
|
+
this.client.emit("cart:cleared");
|
|
525
|
+
return result;
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
var OrdersModule = class {
|
|
529
|
+
constructor(client) {
|
|
530
|
+
this.client = client;
|
|
531
|
+
}
|
|
532
|
+
/** List customer orders (requires auth) */
|
|
533
|
+
async list(options) {
|
|
534
|
+
return this.client.request("GET", "/orders", {
|
|
535
|
+
query: { page: options?.page, limit: options?.limit }
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
/** Get order detail (requires auth) */
|
|
539
|
+
async get(orderNumber) {
|
|
540
|
+
return this.client.request("GET", `/orders/${orderNumber}`);
|
|
541
|
+
}
|
|
542
|
+
/** Cancel a PENDING order (requires auth) */
|
|
543
|
+
async cancel(orderNumber) {
|
|
544
|
+
return this.client.request("POST", `/orders/${orderNumber}/cancel`);
|
|
545
|
+
}
|
|
546
|
+
/** Track order by tracking token (no auth required) */
|
|
547
|
+
async track(trackingToken) {
|
|
548
|
+
return this.client.request("GET", `/orders/track/${trackingToken}`, { auth: false });
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
var CustomerModule = class {
|
|
552
|
+
constructor(client) {
|
|
553
|
+
this.client = client;
|
|
554
|
+
}
|
|
555
|
+
/** Get customer profile */
|
|
556
|
+
async getProfile() {
|
|
557
|
+
return this.client.request("GET", "/customer/profile");
|
|
558
|
+
}
|
|
559
|
+
/** Update customer profile */
|
|
560
|
+
async updateProfile(data) {
|
|
561
|
+
return this.client.request("PATCH", "/customer/profile", { body: data });
|
|
562
|
+
}
|
|
563
|
+
/** Change password */
|
|
564
|
+
async changePassword(currentPassword, newPassword) {
|
|
565
|
+
return this.client.request("PUT", "/customer/password", {
|
|
566
|
+
body: { currentPassword, newPassword }
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
/** List addresses */
|
|
570
|
+
async getAddresses() {
|
|
571
|
+
return this.client.request("GET", "/customer/addresses");
|
|
572
|
+
}
|
|
573
|
+
/** Create address */
|
|
574
|
+
async createAddress(address) {
|
|
575
|
+
return this.client.request("POST", "/customer/addresses", { body: address });
|
|
576
|
+
}
|
|
577
|
+
/** Update address */
|
|
578
|
+
async updateAddress(addressId, data) {
|
|
579
|
+
return this.client.request("PATCH", `/customer/addresses/${addressId}`, { body: data });
|
|
580
|
+
}
|
|
581
|
+
/** Delete address */
|
|
582
|
+
async deleteAddress(addressId) {
|
|
583
|
+
return this.client.request("DELETE", `/customer/addresses/${addressId}`);
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
var PagesModule = class {
|
|
587
|
+
constructor(client) {
|
|
588
|
+
this.client = client;
|
|
589
|
+
}
|
|
590
|
+
/** List CMS pages */
|
|
591
|
+
async list(locale) {
|
|
592
|
+
return this.client.request("GET", "/pages", { query: { locale } });
|
|
593
|
+
}
|
|
594
|
+
/** Get page by slug */
|
|
595
|
+
async get(slug, locale) {
|
|
596
|
+
return this.client.request("GET", `/pages/${slug}`, { query: { locale } });
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
export {
|
|
601
|
+
ProductSort,
|
|
602
|
+
OrderStatuses,
|
|
603
|
+
PaymentStatuses,
|
|
604
|
+
FulfillmentStatuses,
|
|
605
|
+
AddressTypes,
|
|
606
|
+
BehioApiError,
|
|
607
|
+
BehioNetworkError,
|
|
608
|
+
BehioStorefront
|
|
609
|
+
};
|