brainerce 1.0.1 → 1.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/AI_BUILDER_PROMPT.md +740 -720
- package/README.md +210 -607
- package/dist/index.d.mts +158 -99
- package/dist/index.d.ts +158 -99
- package/dist/index.js +436 -132
- package/dist/index.mjs +436 -132
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -121,9 +121,16 @@ var BrainerceClient = class {
|
|
|
121
121
|
constructor(options) {
|
|
122
122
|
this.customerToken = null;
|
|
123
123
|
this.customerCartId = null;
|
|
124
|
+
// Session cart state (server-side guest carts)
|
|
125
|
+
this.sessionCartId = null;
|
|
126
|
+
this.sessionToken = null;
|
|
127
|
+
this._sessionCartPromise = null;
|
|
128
|
+
this._migrationDone = false;
|
|
129
|
+
/** localStorage key for session cart reference (sessionToken + cartId) */
|
|
130
|
+
this.SESSION_CART_KEY = "brainerce_session";
|
|
124
131
|
/**
|
|
125
132
|
* Virtual cart ID used for localStorage carts (guest users not logged in).
|
|
126
|
-
*
|
|
133
|
+
* @deprecated Guest carts now use server-side sessions. Kept for backward compatibility.
|
|
127
134
|
*/
|
|
128
135
|
this.VIRTUAL_LOCAL_CART_ID = "__local__";
|
|
129
136
|
/**
|
|
@@ -155,6 +162,7 @@ var BrainerceClient = class {
|
|
|
155
162
|
this.baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
156
163
|
this.timeout = options.timeout || DEFAULT_TIMEOUT;
|
|
157
164
|
this.onAuthError = options.onAuthError;
|
|
165
|
+
this.hydrateSessionCart();
|
|
158
166
|
}
|
|
159
167
|
withGuards(result, type) {
|
|
160
168
|
if (!isDevGuardsEnabled()) return result;
|
|
@@ -1853,10 +1861,6 @@ var BrainerceClient = class {
|
|
|
1853
1861
|
* ```
|
|
1854
1862
|
*/
|
|
1855
1863
|
async createCart() {
|
|
1856
|
-
if (this.isVibeCodedMode() && !this.isCustomerLoggedIn()) {
|
|
1857
|
-
const localCart = this.getLocalCart();
|
|
1858
|
-
return this.localCartToCart(localCart);
|
|
1859
|
-
}
|
|
1860
1864
|
if (this.isVibeCodedMode()) {
|
|
1861
1865
|
return this.vibeCodedRequest("POST", "/cart");
|
|
1862
1866
|
}
|
|
@@ -1875,6 +1879,9 @@ var BrainerceClient = class {
|
|
|
1875
1879
|
* ```
|
|
1876
1880
|
*/
|
|
1877
1881
|
async getCartBySession(sessionToken) {
|
|
1882
|
+
if (this.isVibeCodedMode()) {
|
|
1883
|
+
return this.vibeCodedRequest("GET", `/cart/session/${sessionToken}`);
|
|
1884
|
+
}
|
|
1878
1885
|
if (this.storeId && !this.apiKey) {
|
|
1879
1886
|
return this.storefrontRequest("GET", `/cart/session/${sessionToken}`);
|
|
1880
1887
|
}
|
|
@@ -1900,6 +1907,7 @@ var BrainerceClient = class {
|
|
|
1900
1907
|
*/
|
|
1901
1908
|
async getCart(cartId) {
|
|
1902
1909
|
if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
1910
|
+
console.warn('getCart("__local__") is deprecated. Use smartGetCart() instead.');
|
|
1903
1911
|
return this.withGuards(this.localCartToCart(this.getLocalCart()), "cart");
|
|
1904
1912
|
}
|
|
1905
1913
|
if (this.isVibeCodedMode()) {
|
|
@@ -1924,38 +1932,14 @@ var BrainerceClient = class {
|
|
|
1924
1932
|
*/
|
|
1925
1933
|
async addToCart(cartId, item) {
|
|
1926
1934
|
if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
1927
|
-
|
|
1928
|
-
let productPrice = "0";
|
|
1929
|
-
let productImage;
|
|
1930
|
-
if (item.productInfo) {
|
|
1931
|
-
productName = item.productInfo.name;
|
|
1932
|
-
productPrice = item.productInfo.price;
|
|
1933
|
-
productImage = item.productInfo.image;
|
|
1934
|
-
} else {
|
|
1935
|
-
try {
|
|
1936
|
-
const product = await this.getProduct(item.productId);
|
|
1937
|
-
productName = product.name;
|
|
1938
|
-
productPrice = product.salePrice || product.basePrice;
|
|
1939
|
-
productImage = product.images?.[0]?.url;
|
|
1940
|
-
if (item.variantId && product.variants) {
|
|
1941
|
-
const variant = product.variants.find((v) => v.id === item.variantId);
|
|
1942
|
-
if (variant) {
|
|
1943
|
-
productPrice = variant.salePrice || variant.price || productPrice;
|
|
1944
|
-
if (variant.image) {
|
|
1945
|
-
productImage = typeof variant.image === "string" ? variant.image : variant.image.url;
|
|
1946
|
-
}
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
} catch {
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1935
|
+
console.warn('addToCart("__local__", ...) is deprecated. Use smartAddToCart() instead.');
|
|
1952
1936
|
this.addToLocalCart({
|
|
1953
1937
|
productId: item.productId,
|
|
1954
1938
|
variantId: item.variantId,
|
|
1955
1939
|
quantity: item.quantity,
|
|
1956
|
-
name:
|
|
1957
|
-
price:
|
|
1958
|
-
image:
|
|
1940
|
+
name: item.productInfo?.name || "Unknown Product",
|
|
1941
|
+
price: item.productInfo?.price || "0",
|
|
1942
|
+
image: item.productInfo?.image
|
|
1959
1943
|
});
|
|
1960
1944
|
return this.withGuards(this.localCartToCart(this.getLocalCart()), "cart");
|
|
1961
1945
|
}
|
|
@@ -1986,6 +1970,9 @@ var BrainerceClient = class {
|
|
|
1986
1970
|
*/
|
|
1987
1971
|
async updateCartItem(cartId, itemId, data) {
|
|
1988
1972
|
if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
1973
|
+
console.warn(
|
|
1974
|
+
'updateCartItem("__local__", ...) is deprecated. Use smartUpdateCartItem() instead.'
|
|
1975
|
+
);
|
|
1989
1976
|
const index = parseInt(itemId.replace("local_", ""), 10);
|
|
1990
1977
|
const localCart = this.getLocalCart();
|
|
1991
1978
|
if (index >= 0 && index < localCart.items.length) {
|
|
@@ -2021,6 +2008,9 @@ var BrainerceClient = class {
|
|
|
2021
2008
|
*/
|
|
2022
2009
|
async removeCartItem(cartId, itemId) {
|
|
2023
2010
|
if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
2011
|
+
console.warn(
|
|
2012
|
+
'removeCartItem("__local__", ...) is deprecated. Use smartRemoveFromCart() instead.'
|
|
2013
|
+
);
|
|
2024
2014
|
const index = parseInt(itemId.replace("local_", ""), 10);
|
|
2025
2015
|
const localCart = this.getLocalCart();
|
|
2026
2016
|
if (index >= 0 && index < localCart.items.length) {
|
|
@@ -2056,6 +2046,9 @@ var BrainerceClient = class {
|
|
|
2056
2046
|
*/
|
|
2057
2047
|
async clearCart(cartId) {
|
|
2058
2048
|
if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
2049
|
+
console.warn(
|
|
2050
|
+
'clearCart("__local__") is deprecated. Use onCheckoutComplete() or clearLocalCart() directly.'
|
|
2051
|
+
);
|
|
2059
2052
|
this.clearLocalCart();
|
|
2060
2053
|
return this.withGuards(this.localCartToCart(this.getLocalCart()), "cart");
|
|
2061
2054
|
}
|
|
@@ -2336,6 +2329,230 @@ var BrainerceClient = class {
|
|
|
2336
2329
|
isCustomerLoggedIn() {
|
|
2337
2330
|
return !!this.customerToken;
|
|
2338
2331
|
}
|
|
2332
|
+
// -------------------- Session Cart Helpers --------------------
|
|
2333
|
+
/**
|
|
2334
|
+
* Hydrate session cart state from localStorage on construction.
|
|
2335
|
+
* @internal
|
|
2336
|
+
*/
|
|
2337
|
+
hydrateSessionCart() {
|
|
2338
|
+
if (!this.isLocalStorageAvailable()) return;
|
|
2339
|
+
try {
|
|
2340
|
+
const stored = window.localStorage.getItem(this.SESSION_CART_KEY);
|
|
2341
|
+
if (stored) {
|
|
2342
|
+
const ref = JSON.parse(stored);
|
|
2343
|
+
this.sessionToken = ref.sessionToken;
|
|
2344
|
+
this.sessionCartId = ref.cartId;
|
|
2345
|
+
}
|
|
2346
|
+
} catch {
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
/**
|
|
2350
|
+
* Persist session cart reference to localStorage.
|
|
2351
|
+
* @internal
|
|
2352
|
+
*/
|
|
2353
|
+
saveSessionCart(ref) {
|
|
2354
|
+
this.sessionToken = ref.sessionToken;
|
|
2355
|
+
this.sessionCartId = ref.cartId;
|
|
2356
|
+
if (!this.isLocalStorageAvailable()) return;
|
|
2357
|
+
window.localStorage.setItem(this.SESSION_CART_KEY, JSON.stringify(ref));
|
|
2358
|
+
}
|
|
2359
|
+
/**
|
|
2360
|
+
* Clear session cart reference from localStorage and memory.
|
|
2361
|
+
* @internal
|
|
2362
|
+
*/
|
|
2363
|
+
clearSessionCart() {
|
|
2364
|
+
this.sessionToken = null;
|
|
2365
|
+
this.sessionCartId = null;
|
|
2366
|
+
this._sessionCartPromise = null;
|
|
2367
|
+
if (!this.isLocalStorageAvailable()) return;
|
|
2368
|
+
window.localStorage.removeItem(this.SESSION_CART_KEY);
|
|
2369
|
+
}
|
|
2370
|
+
/**
|
|
2371
|
+
* Update the cached item count in the session reference.
|
|
2372
|
+
* @internal
|
|
2373
|
+
*/
|
|
2374
|
+
updateSessionCartItemCount(count) {
|
|
2375
|
+
if (!this.sessionToken || !this.sessionCartId) return;
|
|
2376
|
+
this.saveSessionCart({
|
|
2377
|
+
sessionToken: this.sessionToken,
|
|
2378
|
+
cartId: this.sessionCartId,
|
|
2379
|
+
itemCount: count
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
/**
|
|
2383
|
+
* Return a synthetic empty Cart object for display when no server cart exists yet.
|
|
2384
|
+
* Avoids creating a server cart just to show "your cart is empty."
|
|
2385
|
+
* @internal
|
|
2386
|
+
*/
|
|
2387
|
+
emptyCart() {
|
|
2388
|
+
return {
|
|
2389
|
+
id: "",
|
|
2390
|
+
sessionToken: null,
|
|
2391
|
+
customerId: null,
|
|
2392
|
+
status: "ACTIVE",
|
|
2393
|
+
currency: "USD",
|
|
2394
|
+
notes: null,
|
|
2395
|
+
subtotal: "0",
|
|
2396
|
+
discountAmount: "0",
|
|
2397
|
+
couponCode: null,
|
|
2398
|
+
items: [],
|
|
2399
|
+
itemCount: 0,
|
|
2400
|
+
expiresAt: null,
|
|
2401
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2402
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2403
|
+
};
|
|
2404
|
+
}
|
|
2405
|
+
/**
|
|
2406
|
+
* Create a server-side cart via API. Never falls back to localStorage.
|
|
2407
|
+
* @internal
|
|
2408
|
+
*/
|
|
2409
|
+
async createServerCart() {
|
|
2410
|
+
if (this.isVibeCodedMode()) {
|
|
2411
|
+
return this.vibeCodedRequest("POST", "/cart");
|
|
2412
|
+
}
|
|
2413
|
+
if (this.storeId && !this.apiKey) {
|
|
2414
|
+
return this.storefrontRequest("POST", "/cart");
|
|
2415
|
+
}
|
|
2416
|
+
return this.adminRequest("POST", "/api/v1/cart");
|
|
2417
|
+
}
|
|
2418
|
+
/**
|
|
2419
|
+
* Get or create a server-side session cart for guest users.
|
|
2420
|
+
* Lazily creates the cart on first call (e.g., first add-to-cart).
|
|
2421
|
+
* Uses promise dedup lock to prevent race conditions on parallel calls.
|
|
2422
|
+
* @internal
|
|
2423
|
+
*/
|
|
2424
|
+
async getOrCreateSessionCart() {
|
|
2425
|
+
if (this._sessionCartPromise) return this._sessionCartPromise;
|
|
2426
|
+
this._sessionCartPromise = this._getOrCreateSessionCartImpl();
|
|
2427
|
+
try {
|
|
2428
|
+
return await this._sessionCartPromise;
|
|
2429
|
+
} finally {
|
|
2430
|
+
this._sessionCartPromise = null;
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
/** @internal */
|
|
2434
|
+
async _getOrCreateSessionCartImpl() {
|
|
2435
|
+
const migrated = await this.migrateLocalCartToSession();
|
|
2436
|
+
if (migrated) return migrated;
|
|
2437
|
+
if (this.sessionCartId && this.sessionToken) {
|
|
2438
|
+
try {
|
|
2439
|
+
const cart2 = await this.getCart(this.sessionCartId);
|
|
2440
|
+
if (cart2.status === "ACTIVE") {
|
|
2441
|
+
return cart2;
|
|
2442
|
+
}
|
|
2443
|
+
this.clearSessionCart();
|
|
2444
|
+
} catch {
|
|
2445
|
+
this.clearSessionCart();
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
if (this.sessionToken) {
|
|
2449
|
+
try {
|
|
2450
|
+
const cart2 = await this.getCartBySession(this.sessionToken);
|
|
2451
|
+
if (cart2 && cart2.status === "ACTIVE") {
|
|
2452
|
+
this.saveSessionCart({
|
|
2453
|
+
sessionToken: this.sessionToken,
|
|
2454
|
+
cartId: cart2.id,
|
|
2455
|
+
itemCount: cart2.itemCount
|
|
2456
|
+
});
|
|
2457
|
+
return cart2;
|
|
2458
|
+
}
|
|
2459
|
+
} catch {
|
|
2460
|
+
this.clearSessionCart();
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
const cart = await this.createServerCart();
|
|
2464
|
+
if (cart.sessionToken) {
|
|
2465
|
+
this.saveSessionCart({
|
|
2466
|
+
sessionToken: cart.sessionToken,
|
|
2467
|
+
cartId: cart.id,
|
|
2468
|
+
itemCount: cart.itemCount
|
|
2469
|
+
});
|
|
2470
|
+
}
|
|
2471
|
+
return cart;
|
|
2472
|
+
}
|
|
2473
|
+
/**
|
|
2474
|
+
* Migrate legacy localStorage cart to server session cart.
|
|
2475
|
+
* Called once, on first smart method call if migration is needed.
|
|
2476
|
+
* @internal
|
|
2477
|
+
*/
|
|
2478
|
+
async migrateLocalCartToSession() {
|
|
2479
|
+
if (this._migrationDone) return null;
|
|
2480
|
+
this._migrationDone = true;
|
|
2481
|
+
if (this.sessionToken || this.sessionCartId) return null;
|
|
2482
|
+
const localCart = this.getLocalCart();
|
|
2483
|
+
if (localCart.items.length === 0) return null;
|
|
2484
|
+
try {
|
|
2485
|
+
const cart = await this.createServerCart();
|
|
2486
|
+
for (const item of localCart.items) {
|
|
2487
|
+
try {
|
|
2488
|
+
await this.addToCart(cart.id, {
|
|
2489
|
+
productId: item.productId,
|
|
2490
|
+
variantId: item.variantId,
|
|
2491
|
+
quantity: item.quantity
|
|
2492
|
+
});
|
|
2493
|
+
} catch {
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
if (cart.sessionToken) {
|
|
2497
|
+
this.saveSessionCart({
|
|
2498
|
+
sessionToken: cart.sessionToken,
|
|
2499
|
+
cartId: cart.id
|
|
2500
|
+
});
|
|
2501
|
+
}
|
|
2502
|
+
this.clearLocalCart();
|
|
2503
|
+
return this.getCart(cart.id);
|
|
2504
|
+
} catch {
|
|
2505
|
+
this._migrationDone = false;
|
|
2506
|
+
return null;
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
/**
|
|
2510
|
+
* Merge the guest session cart into the customer's cart on the server.
|
|
2511
|
+
* Tries linkCart first (fast, single call), falls back to mergeCarts endpoint.
|
|
2512
|
+
* @internal
|
|
2513
|
+
*/
|
|
2514
|
+
async mergeSessionCartOnLogin() {
|
|
2515
|
+
if (this.sessionCartId) {
|
|
2516
|
+
try {
|
|
2517
|
+
return await this.linkCart(this.sessionCartId);
|
|
2518
|
+
} catch {
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
if (this.isVibeCodedMode()) {
|
|
2522
|
+
return this.vibeCodedRequest("POST", "/cart/merge", {
|
|
2523
|
+
sessionToken: this.sessionToken
|
|
2524
|
+
});
|
|
2525
|
+
}
|
|
2526
|
+
if (this.storeId && !this.apiKey) {
|
|
2527
|
+
return this.storefrontRequest("POST", "/cart/merge", {
|
|
2528
|
+
sessionToken: this.sessionToken
|
|
2529
|
+
});
|
|
2530
|
+
}
|
|
2531
|
+
return this.adminRequest("POST", "/api/v1/cart/merge", {
|
|
2532
|
+
sessionToken: this.sessionToken
|
|
2533
|
+
});
|
|
2534
|
+
}
|
|
2535
|
+
/**
|
|
2536
|
+
* Get the cart item count without a full cart fetch.
|
|
2537
|
+
* Returns the cached count from localStorage if available, or 0.
|
|
2538
|
+
* For accurate counts, use smartGetCart() and read cart.itemCount.
|
|
2539
|
+
*/
|
|
2540
|
+
getSmartCartItemCount() {
|
|
2541
|
+
if (this.isCustomerLoggedIn()) {
|
|
2542
|
+
return 0;
|
|
2543
|
+
}
|
|
2544
|
+
if (!this.isLocalStorageAvailable()) return 0;
|
|
2545
|
+
try {
|
|
2546
|
+
const stored = window.localStorage.getItem(this.SESSION_CART_KEY);
|
|
2547
|
+
if (stored) {
|
|
2548
|
+
const ref = JSON.parse(stored);
|
|
2549
|
+
return ref.itemCount ?? 0;
|
|
2550
|
+
}
|
|
2551
|
+
} catch {
|
|
2552
|
+
}
|
|
2553
|
+
return 0;
|
|
2554
|
+
}
|
|
2555
|
+
// -------------------- Customer Cart --------------------
|
|
2339
2556
|
/**
|
|
2340
2557
|
* Get or create a server cart for the logged-in customer
|
|
2341
2558
|
* Caches the cart ID for subsequent calls
|
|
@@ -2388,10 +2605,10 @@ var BrainerceClient = class {
|
|
|
2388
2605
|
throw new BrainerceError("fetchCustomerCart requires vibe-coded or storefront mode", 400);
|
|
2389
2606
|
}
|
|
2390
2607
|
/**
|
|
2391
|
-
* Smart add to cart - automatically uses
|
|
2608
|
+
* Smart add to cart - automatically uses the correct cart based on auth state
|
|
2392
2609
|
*
|
|
2393
|
-
* - **
|
|
2394
|
-
* - **
|
|
2610
|
+
* - **Logged in**: Uses customer's server cart
|
|
2611
|
+
* - **Guest**: Uses server-side session cart (creates one if needed)
|
|
2395
2612
|
*
|
|
2396
2613
|
* @example
|
|
2397
2614
|
* ```typescript
|
|
@@ -2399,25 +2616,35 @@ var BrainerceClient = class {
|
|
|
2399
2616
|
* await client.smartAddToCart({
|
|
2400
2617
|
* productId: 'prod_123',
|
|
2401
2618
|
* quantity: 2,
|
|
2402
|
-
* name: 'Cool Product', // Optional: for localStorage display
|
|
2403
|
-
* price: '29.99',
|
|
2404
2619
|
* });
|
|
2405
2620
|
* ```
|
|
2406
2621
|
*/
|
|
2407
2622
|
async smartAddToCart(item) {
|
|
2408
2623
|
if (this.isCustomerLoggedIn()) {
|
|
2409
2624
|
const cart = await this.getOrCreateCustomerCart();
|
|
2410
|
-
|
|
2625
|
+
const updated = await this.addToCart(cart.id, {
|
|
2411
2626
|
productId: item.productId,
|
|
2412
2627
|
variantId: item.variantId,
|
|
2413
2628
|
quantity: item.quantity
|
|
2414
2629
|
});
|
|
2630
|
+
return updated;
|
|
2415
2631
|
} else {
|
|
2416
|
-
|
|
2632
|
+
const cart = await this.getOrCreateSessionCart();
|
|
2633
|
+
const updated = await this.addToCart(cart.id, {
|
|
2634
|
+
productId: item.productId,
|
|
2635
|
+
variantId: item.variantId,
|
|
2636
|
+
quantity: item.quantity
|
|
2637
|
+
});
|
|
2638
|
+
this.updateSessionCartItemCount(updated.items?.length ?? 0);
|
|
2639
|
+
return updated;
|
|
2417
2640
|
}
|
|
2418
2641
|
}
|
|
2419
2642
|
/**
|
|
2420
|
-
* Smart get cart - returns
|
|
2643
|
+
* Smart get cart - returns the current cart (server-side for both guests and logged-in users)
|
|
2644
|
+
*
|
|
2645
|
+
* - **Logged in**: Returns customer's server cart
|
|
2646
|
+
* - **Guest with session**: Returns server-side session cart
|
|
2647
|
+
* - **Guest without session**: Returns empty cart (no server call, cart created lazily on add)
|
|
2421
2648
|
*
|
|
2422
2649
|
* @example
|
|
2423
2650
|
* ```typescript
|
|
@@ -2429,7 +2656,10 @@ var BrainerceClient = class {
|
|
|
2429
2656
|
if (this.isCustomerLoggedIn()) {
|
|
2430
2657
|
return this.getOrCreateCustomerCart();
|
|
2431
2658
|
} else {
|
|
2432
|
-
|
|
2659
|
+
if (!this.sessionToken) {
|
|
2660
|
+
return this.emptyCart();
|
|
2661
|
+
}
|
|
2662
|
+
return this.getOrCreateSessionCart();
|
|
2433
2663
|
}
|
|
2434
2664
|
}
|
|
2435
2665
|
/**
|
|
@@ -2455,7 +2685,24 @@ var BrainerceClient = class {
|
|
|
2455
2685
|
}
|
|
2456
2686
|
return this.updateCartItem(cart.id, item.id, { quantity });
|
|
2457
2687
|
} else {
|
|
2458
|
-
|
|
2688
|
+
if (!this.sessionToken) {
|
|
2689
|
+
return this.emptyCart();
|
|
2690
|
+
}
|
|
2691
|
+
const cart = await this.getOrCreateSessionCart();
|
|
2692
|
+
const item = cart.items.find(
|
|
2693
|
+
(i) => i.productId === productId && i.variantId === (variantId ?? null)
|
|
2694
|
+
);
|
|
2695
|
+
if (!item) {
|
|
2696
|
+
return cart;
|
|
2697
|
+
}
|
|
2698
|
+
let updated;
|
|
2699
|
+
if (quantity <= 0) {
|
|
2700
|
+
updated = await this.removeCartItem(cart.id, item.id);
|
|
2701
|
+
} else {
|
|
2702
|
+
updated = await this.updateCartItem(cart.id, item.id, { quantity });
|
|
2703
|
+
}
|
|
2704
|
+
this.updateSessionCartItemCount(updated.items?.length ?? 0);
|
|
2705
|
+
return updated;
|
|
2459
2706
|
}
|
|
2460
2707
|
}
|
|
2461
2708
|
/**
|
|
@@ -2471,15 +2718,13 @@ var BrainerceClient = class {
|
|
|
2471
2718
|
return this.smartUpdateCartItem(productId, 0, variantId);
|
|
2472
2719
|
}
|
|
2473
2720
|
/**
|
|
2474
|
-
* Sync
|
|
2721
|
+
* Sync guest cart to customer cart on login
|
|
2475
2722
|
*
|
|
2476
2723
|
* Call this AFTER setCustomerToken() when a customer logs in.
|
|
2477
2724
|
* This will:
|
|
2478
|
-
* 1.
|
|
2479
|
-
* 2.
|
|
2480
|
-
* 3. Clear localStorage
|
|
2481
|
-
*
|
|
2482
|
-
* Items that fail to sync (e.g., out of stock) are silently skipped.
|
|
2725
|
+
* 1. If a server-side session cart exists, merge it into the customer cart (atomic, server-side)
|
|
2726
|
+
* 2. If only a legacy localStorage cart exists, sync items one by one (backward compat)
|
|
2727
|
+
* 3. Clear the session/localStorage after merge
|
|
2483
2728
|
*
|
|
2484
2729
|
* @example
|
|
2485
2730
|
* ```typescript
|
|
@@ -2487,7 +2732,7 @@ var BrainerceClient = class {
|
|
|
2487
2732
|
* const auth = await client.login(email, password);
|
|
2488
2733
|
* client.setCustomerToken(auth.token);
|
|
2489
2734
|
*
|
|
2490
|
-
* //
|
|
2735
|
+
* // Merge their guest cart into customer cart
|
|
2491
2736
|
* const cart = await client.syncCartOnLogin();
|
|
2492
2737
|
* console.log('Cart synced, items:', cart.items.length);
|
|
2493
2738
|
* ```
|
|
@@ -2499,6 +2744,14 @@ var BrainerceClient = class {
|
|
|
2499
2744
|
401
|
|
2500
2745
|
);
|
|
2501
2746
|
}
|
|
2747
|
+
if (this.sessionToken) {
|
|
2748
|
+
try {
|
|
2749
|
+
const merged = await this.mergeSessionCartOnLogin();
|
|
2750
|
+
this.clearSessionCart();
|
|
2751
|
+
return merged;
|
|
2752
|
+
} catch {
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2502
2755
|
const localCart = this.getLocalCart();
|
|
2503
2756
|
const serverCart = await this.getOrCreateCustomerCart();
|
|
2504
2757
|
if (localCart.items.length === 0) {
|
|
@@ -2520,15 +2773,15 @@ var BrainerceClient = class {
|
|
|
2520
2773
|
/**
|
|
2521
2774
|
* Clear cart state on logout
|
|
2522
2775
|
*
|
|
2523
|
-
* Call this when a customer logs out to clear the cached cart ID.
|
|
2524
|
-
* The
|
|
2776
|
+
* Call this when a customer logs out to clear the cached customer cart ID.
|
|
2777
|
+
* The session cart (if any) remains available for the next guest session.
|
|
2525
2778
|
*
|
|
2526
2779
|
* @example
|
|
2527
2780
|
* ```typescript
|
|
2528
2781
|
* client.clearCustomerToken();
|
|
2529
2782
|
* client.onLogout();
|
|
2530
2783
|
*
|
|
2531
|
-
* // Now back to guest mode - cart uses
|
|
2784
|
+
* // Now back to guest mode - cart uses server-side session
|
|
2532
2785
|
* await client.smartAddToCart({ productId: 'prod_123', quantity: 1 });
|
|
2533
2786
|
* ```
|
|
2534
2787
|
*/
|
|
@@ -2546,12 +2799,12 @@ var BrainerceClient = class {
|
|
|
2546
2799
|
* // After payment success
|
|
2547
2800
|
* if (paymentStatus === 'succeeded') {
|
|
2548
2801
|
* client.onCheckoutComplete();
|
|
2549
|
-
* client.clearLocalCart(); // Also clear localStorage for guests
|
|
2550
2802
|
* }
|
|
2551
2803
|
* ```
|
|
2552
2804
|
*/
|
|
2553
2805
|
onCheckoutComplete() {
|
|
2554
2806
|
this.customerCartId = null;
|
|
2807
|
+
this.clearSessionCart();
|
|
2555
2808
|
}
|
|
2556
2809
|
// -------------------- Checkout --------------------
|
|
2557
2810
|
/**
|
|
@@ -2578,7 +2831,7 @@ var BrainerceClient = class {
|
|
|
2578
2831
|
async createCheckout(data) {
|
|
2579
2832
|
if (data.cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
2580
2833
|
throw new BrainerceError(
|
|
2581
|
-
|
|
2834
|
+
'Cannot create checkout from "__local__" cart. Use startGuestCheckout() or smartAddToCart() to create a server-side cart first.',
|
|
2582
2835
|
400
|
|
2583
2836
|
);
|
|
2584
2837
|
}
|
|
@@ -2647,6 +2900,46 @@ var BrainerceClient = class {
|
|
|
2647
2900
|
}
|
|
2648
2901
|
return this.adminRequest("PATCH", `/api/v1/checkout/${checkoutId}/customer`, data);
|
|
2649
2902
|
}
|
|
2903
|
+
/**
|
|
2904
|
+
* Get available shipping destinations for this store.
|
|
2905
|
+
*
|
|
2906
|
+
* Returns which countries and regions the store ships to,
|
|
2907
|
+
* so you can populate `<select>` dropdowns in your checkout form.
|
|
2908
|
+
*
|
|
2909
|
+
* @example
|
|
2910
|
+
* ```typescript
|
|
2911
|
+
* const dest = await client.getShippingDestinations();
|
|
2912
|
+
*
|
|
2913
|
+
* if (dest.worldwide) {
|
|
2914
|
+
* // Store ships everywhere — use a full country list
|
|
2915
|
+
* } else {
|
|
2916
|
+
* // Only show available countries
|
|
2917
|
+
* dest.countries.forEach(c => console.log(c.code, c.name));
|
|
2918
|
+
* }
|
|
2919
|
+
*
|
|
2920
|
+
* // Check region restrictions for a country
|
|
2921
|
+
* const usRegions = dest.regions['US'];
|
|
2922
|
+
* if (usRegions) {
|
|
2923
|
+
* // Only these US states are available
|
|
2924
|
+
* usRegions.forEach(r => console.log(r.code, r.name));
|
|
2925
|
+
* }
|
|
2926
|
+
* ```
|
|
2927
|
+
*/
|
|
2928
|
+
async getShippingDestinations() {
|
|
2929
|
+
if (this.isVibeCodedMode()) {
|
|
2930
|
+
return this.vibeCodedRequest("GET", "/shipping/destinations");
|
|
2931
|
+
}
|
|
2932
|
+
if (this.storeId && !this.apiKey) {
|
|
2933
|
+
return this.storefrontRequest(
|
|
2934
|
+
"GET",
|
|
2935
|
+
"/checkouts/shipping-destinations"
|
|
2936
|
+
);
|
|
2937
|
+
}
|
|
2938
|
+
return this.adminRequest(
|
|
2939
|
+
"GET",
|
|
2940
|
+
"/api/v1/checkouts/shipping-destinations"
|
|
2941
|
+
);
|
|
2942
|
+
}
|
|
2650
2943
|
/**
|
|
2651
2944
|
* Set shipping address on checkout (includes customer email).
|
|
2652
2945
|
* Returns the checkout and available shipping rates for the address.
|
|
@@ -3114,14 +3407,8 @@ var BrainerceClient = class {
|
|
|
3114
3407
|
}
|
|
3115
3408
|
}
|
|
3116
3409
|
/**
|
|
3117
|
-
*
|
|
3118
|
-
* Returns empty cart if none exists
|
|
3119
|
-
*
|
|
3120
|
-
* @example
|
|
3121
|
-
* ```typescript
|
|
3122
|
-
* const cart = client.getLocalCart();
|
|
3123
|
-
* console.log('Items in cart:', cart.items.length);
|
|
3124
|
-
* ```
|
|
3410
|
+
* @deprecated Use `smartGetCart()` instead. Guest carts are now stored on the server.
|
|
3411
|
+
* Get local cart from localStorage. Returns empty cart if none exists.
|
|
3125
3412
|
*/
|
|
3126
3413
|
getLocalCart() {
|
|
3127
3414
|
if (!this.isLocalStorageAvailable()) {
|
|
@@ -3203,18 +3490,8 @@ var BrainerceClient = class {
|
|
|
3203
3490
|
};
|
|
3204
3491
|
}
|
|
3205
3492
|
/**
|
|
3206
|
-
*
|
|
3207
|
-
* If item already exists, updates quantity
|
|
3208
|
-
*
|
|
3209
|
-
* @example
|
|
3210
|
-
* ```typescript
|
|
3211
|
-
* client.addToLocalCart({
|
|
3212
|
-
* productId: 'prod_123',
|
|
3213
|
-
* quantity: 2,
|
|
3214
|
-
* name: 'Cool Shirt',
|
|
3215
|
-
* price: '29.99',
|
|
3216
|
-
* });
|
|
3217
|
-
* ```
|
|
3493
|
+
* @deprecated Use `smartAddToCart()` instead. Guest carts are now stored on the server.
|
|
3494
|
+
* Add item to local cart (NO API call). If item already exists, updates quantity.
|
|
3218
3495
|
*/
|
|
3219
3496
|
addToLocalCart(item) {
|
|
3220
3497
|
const cart = this.getLocalCart();
|
|
@@ -3236,14 +3513,8 @@ var BrainerceClient = class {
|
|
|
3236
3513
|
return cart;
|
|
3237
3514
|
}
|
|
3238
3515
|
/**
|
|
3239
|
-
*
|
|
3240
|
-
* Set quantity to 0 to remove item
|
|
3241
|
-
*
|
|
3242
|
-
* @example
|
|
3243
|
-
* ```typescript
|
|
3244
|
-
* client.updateLocalCartItem('prod_123', 3); // Set quantity to 3
|
|
3245
|
-
* client.updateLocalCartItem('prod_123', 0); // Remove item
|
|
3246
|
-
* ```
|
|
3516
|
+
* @deprecated Use `smartUpdateCartItem()` instead. Guest carts are now stored on the server.
|
|
3517
|
+
* Update item quantity in local cart. Set quantity to 0 to remove item.
|
|
3247
3518
|
*/
|
|
3248
3519
|
updateLocalCartItem(productId, quantity, variantId) {
|
|
3249
3520
|
const cart = this.getLocalCart();
|
|
@@ -3261,24 +3532,13 @@ var BrainerceClient = class {
|
|
|
3261
3532
|
return cart;
|
|
3262
3533
|
}
|
|
3263
3534
|
/**
|
|
3264
|
-
*
|
|
3265
|
-
*
|
|
3266
|
-
* @example
|
|
3267
|
-
* ```typescript
|
|
3268
|
-
* client.removeFromLocalCart('prod_123');
|
|
3269
|
-
* client.removeFromLocalCart('prod_456', 'variant_789');
|
|
3270
|
-
* ```
|
|
3535
|
+
* @deprecated Use `smartRemoveFromCart()` instead. Guest carts are now stored on the server.
|
|
3271
3536
|
*/
|
|
3272
3537
|
removeFromLocalCart(productId, variantId) {
|
|
3273
3538
|
return this.updateLocalCartItem(productId, 0, variantId);
|
|
3274
3539
|
}
|
|
3275
3540
|
/**
|
|
3276
|
-
*
|
|
3277
|
-
*
|
|
3278
|
-
* @example
|
|
3279
|
-
* ```typescript
|
|
3280
|
-
* client.clearLocalCart();
|
|
3281
|
-
* ```
|
|
3541
|
+
* @deprecated Use `onCheckoutComplete()` instead. Guest carts are now stored on the server.
|
|
3282
3542
|
*/
|
|
3283
3543
|
clearLocalCart() {
|
|
3284
3544
|
const cart = { items: [], updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -3286,16 +3546,8 @@ var BrainerceClient = class {
|
|
|
3286
3546
|
return cart;
|
|
3287
3547
|
}
|
|
3288
3548
|
/**
|
|
3289
|
-
*
|
|
3290
|
-
*
|
|
3291
|
-
* @example
|
|
3292
|
-
* ```typescript
|
|
3293
|
-
* client.setLocalCartCustomer({
|
|
3294
|
-
* email: 'john@example.com',
|
|
3295
|
-
* firstName: 'John',
|
|
3296
|
-
* lastName: 'Doe',
|
|
3297
|
-
* });
|
|
3298
|
-
* ```
|
|
3549
|
+
* @deprecated Customer info is now set via checkout address methods.
|
|
3550
|
+
* Still used by `submitGuestOrder()` for backward compatibility.
|
|
3299
3551
|
*/
|
|
3300
3552
|
setLocalCartCustomer(customer) {
|
|
3301
3553
|
const cart = this.getLocalCart();
|
|
@@ -3304,19 +3556,8 @@ var BrainerceClient = class {
|
|
|
3304
3556
|
return cart;
|
|
3305
3557
|
}
|
|
3306
3558
|
/**
|
|
3307
|
-
*
|
|
3308
|
-
*
|
|
3309
|
-
* @example
|
|
3310
|
-
* ```typescript
|
|
3311
|
-
* client.setLocalCartShippingAddress({
|
|
3312
|
-
* firstName: 'John',
|
|
3313
|
-
* lastName: 'Doe',
|
|
3314
|
-
* line1: '123 Main St',
|
|
3315
|
-
* city: 'Tel Aviv',
|
|
3316
|
-
* postalCode: '6100000',
|
|
3317
|
-
* country: 'IL',
|
|
3318
|
-
* });
|
|
3319
|
-
* ```
|
|
3559
|
+
* @deprecated Address is now set via `updateGuestCheckoutAddress()` after `startGuestCheckout()`.
|
|
3560
|
+
* Still used by `submitGuestOrder()` for backward compatibility.
|
|
3320
3561
|
*/
|
|
3321
3562
|
setLocalCartShippingAddress(address) {
|
|
3322
3563
|
const cart = this.getLocalCart();
|
|
@@ -3325,7 +3566,8 @@ var BrainerceClient = class {
|
|
|
3325
3566
|
return cart;
|
|
3326
3567
|
}
|
|
3327
3568
|
/**
|
|
3328
|
-
*
|
|
3569
|
+
* @deprecated Address is now set via `updateGuestCheckoutAddress()` after `startGuestCheckout()`.
|
|
3570
|
+
* Still used by `submitGuestOrder()` for backward compatibility.
|
|
3329
3571
|
*/
|
|
3330
3572
|
setLocalCartBillingAddress(address) {
|
|
3331
3573
|
const cart = this.getLocalCart();
|
|
@@ -3334,7 +3576,7 @@ var BrainerceClient = class {
|
|
|
3334
3576
|
return cart;
|
|
3335
3577
|
}
|
|
3336
3578
|
/**
|
|
3337
|
-
*
|
|
3579
|
+
* @deprecated Coupons are now applied via `applyCoupon()` on the server cart.
|
|
3338
3580
|
*/
|
|
3339
3581
|
setLocalCartCoupon(couponCode) {
|
|
3340
3582
|
const cart = this.getLocalCart();
|
|
@@ -3343,7 +3585,7 @@ var BrainerceClient = class {
|
|
|
3343
3585
|
return cart;
|
|
3344
3586
|
}
|
|
3345
3587
|
/**
|
|
3346
|
-
*
|
|
3588
|
+
* @deprecated Use `getSmartCartItemCount()` instead.
|
|
3347
3589
|
*/
|
|
3348
3590
|
getLocalCartItemCount() {
|
|
3349
3591
|
const cart = this.getLocalCart();
|
|
@@ -3373,6 +3615,46 @@ var BrainerceClient = class {
|
|
|
3373
3615
|
* ```
|
|
3374
3616
|
*/
|
|
3375
3617
|
async submitGuestOrder(options) {
|
|
3618
|
+
if (this.sessionToken) {
|
|
3619
|
+
const serverCart = await this.getOrCreateSessionCart();
|
|
3620
|
+
if (!serverCart.items || serverCart.items.length === 0) {
|
|
3621
|
+
throw new BrainerceError("Cart is empty", 400);
|
|
3622
|
+
}
|
|
3623
|
+
const localCart = this.getLocalCart();
|
|
3624
|
+
if (!localCart.customer?.email) {
|
|
3625
|
+
throw new BrainerceError(
|
|
3626
|
+
"Customer email is required. Call setLocalCartCustomer() first.",
|
|
3627
|
+
400
|
|
3628
|
+
);
|
|
3629
|
+
}
|
|
3630
|
+
if (!localCart.shippingAddress) {
|
|
3631
|
+
throw new BrainerceError(
|
|
3632
|
+
"Shipping address is required. Call setLocalCartShippingAddress() first.",
|
|
3633
|
+
400
|
|
3634
|
+
);
|
|
3635
|
+
}
|
|
3636
|
+
const trackingResult = await this.startGuestCheckout();
|
|
3637
|
+
if (!trackingResult.tracked) {
|
|
3638
|
+
throw new BrainerceError("Failed to create checkout from session cart", 500);
|
|
3639
|
+
}
|
|
3640
|
+
await this.updateGuestCheckoutAddress(trackingResult.checkoutId, {
|
|
3641
|
+
shippingAddress: {
|
|
3642
|
+
...localCart.shippingAddress,
|
|
3643
|
+
email: localCart.customer.email
|
|
3644
|
+
},
|
|
3645
|
+
billingAddress: localCart.billingAddress
|
|
3646
|
+
});
|
|
3647
|
+
const orderResult = await this.completeGuestCheckout(trackingResult.checkoutId, {
|
|
3648
|
+
clearCartOnSuccess: options?.clearCartOnSuccess
|
|
3649
|
+
});
|
|
3650
|
+
return {
|
|
3651
|
+
orderId: orderResult.orderId,
|
|
3652
|
+
orderNumber: orderResult.orderNumber || orderResult.orderId,
|
|
3653
|
+
status: orderResult.status || "pending",
|
|
3654
|
+
total: orderResult.total ?? 0,
|
|
3655
|
+
message: orderResult.message || "Order created successfully"
|
|
3656
|
+
};
|
|
3657
|
+
}
|
|
3376
3658
|
const cart = this.getLocalCart();
|
|
3377
3659
|
if (cart.items.length === 0) {
|
|
3378
3660
|
throw new BrainerceError("Cart is empty", 400);
|
|
@@ -3390,7 +3672,6 @@ var BrainerceClient = class {
|
|
|
3390
3672
|
shippingAddress: {
|
|
3391
3673
|
...cart.shippingAddress,
|
|
3392
3674
|
email: cart.customer.email
|
|
3393
|
-
// Already validated above
|
|
3394
3675
|
},
|
|
3395
3676
|
billingAddress: cart.billingAddress
|
|
3396
3677
|
});
|
|
@@ -3478,6 +3759,34 @@ var BrainerceClient = class {
|
|
|
3478
3759
|
* ```
|
|
3479
3760
|
*/
|
|
3480
3761
|
async startGuestCheckout(options) {
|
|
3762
|
+
if (this.sessionToken) {
|
|
3763
|
+
const serverCart = await this.getOrCreateSessionCart();
|
|
3764
|
+
if (!serverCart.items || serverCart.items.length === 0) {
|
|
3765
|
+
throw new BrainerceError("Cart is empty", 400);
|
|
3766
|
+
}
|
|
3767
|
+
let selectedItemIds;
|
|
3768
|
+
if (options?.selectedIndices?.length) {
|
|
3769
|
+
selectedItemIds = options.selectedIndices.map((i) => serverCart.items[i]?.id).filter((id) => !!id);
|
|
3770
|
+
if (selectedItemIds.length === 0) {
|
|
3771
|
+
throw new BrainerceError("No items selected for checkout", 400);
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
const checkout = await this.createCheckout({
|
|
3775
|
+
cartId: serverCart.id,
|
|
3776
|
+
selectedItemIds
|
|
3777
|
+
});
|
|
3778
|
+
this.saveActiveCheckout({
|
|
3779
|
+
checkoutId: checkout.id,
|
|
3780
|
+
cartId: serverCart.id,
|
|
3781
|
+
selectedIndices: options?.selectedIndices
|
|
3782
|
+
});
|
|
3783
|
+
return {
|
|
3784
|
+
tracked: true,
|
|
3785
|
+
checkoutId: checkout.id,
|
|
3786
|
+
cartId: serverCart.id,
|
|
3787
|
+
message: "Checkout created from session cart"
|
|
3788
|
+
};
|
|
3789
|
+
}
|
|
3481
3790
|
const cart = this.getLocalCart();
|
|
3482
3791
|
if (cart.items.length === 0) {
|
|
3483
3792
|
throw new BrainerceError("Cart is empty", 400);
|
|
@@ -3541,16 +3850,8 @@ var BrainerceClient = class {
|
|
|
3541
3850
|
}
|
|
3542
3851
|
}
|
|
3543
3852
|
/**
|
|
3853
|
+
* @deprecated Partial checkout is now handled server-side via `selectedItemIds`.
|
|
3544
3854
|
* Remove specific items from local cart by their indices.
|
|
3545
|
-
* Use after partial checkout to remove only the purchased items.
|
|
3546
|
-
*
|
|
3547
|
-
* @param indices - Array of item indices to remove
|
|
3548
|
-
*
|
|
3549
|
-
* @example
|
|
3550
|
-
* ```typescript
|
|
3551
|
-
* // After partial checkout success, remove purchased items
|
|
3552
|
-
* client.removeLocalCartItemsByIndex([0, 2]); // Removes items at index 0 and 2
|
|
3553
|
-
* ```
|
|
3554
3855
|
*/
|
|
3555
3856
|
removeLocalCartItemsByIndex(indices) {
|
|
3556
3857
|
const cart = this.getLocalCart();
|
|
@@ -3627,6 +3928,7 @@ var BrainerceClient = class {
|
|
|
3627
3928
|
this.removeLocalCartItemsByIndex(indices);
|
|
3628
3929
|
} else {
|
|
3629
3930
|
this.clearLocalCart();
|
|
3931
|
+
this.clearSessionCart();
|
|
3630
3932
|
}
|
|
3631
3933
|
this.customerCartId = null;
|
|
3632
3934
|
}
|
|
@@ -3724,6 +4026,7 @@ var BrainerceClient = class {
|
|
|
3724
4026
|
}
|
|
3725
4027
|
if (isLoggedIn) {
|
|
3726
4028
|
this.clearLocalCart();
|
|
4029
|
+
this.clearSessionCart();
|
|
3727
4030
|
return {
|
|
3728
4031
|
cleared: true,
|
|
3729
4032
|
mode: "full",
|
|
@@ -3740,6 +4043,7 @@ var BrainerceClient = class {
|
|
|
3740
4043
|
};
|
|
3741
4044
|
}
|
|
3742
4045
|
this.clearLocalCart();
|
|
4046
|
+
this.clearSessionCart();
|
|
3743
4047
|
return {
|
|
3744
4048
|
cleared: true,
|
|
3745
4049
|
mode: "full",
|