brainerce 1.0.1 → 1.0.2
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 +8 -28
- package/README.md +211 -608
- package/dist/index.d.mts +106 -98
- package/dist/index.d.ts +106 -98
- package/dist/index.js +384 -132
- package/dist/index.mjs +384 -132
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -179,9 +179,16 @@ var BrainerceClient = class {
|
|
|
179
179
|
constructor(options) {
|
|
180
180
|
this.customerToken = null;
|
|
181
181
|
this.customerCartId = null;
|
|
182
|
+
// Session cart state (server-side guest carts)
|
|
183
|
+
this.sessionCartId = null;
|
|
184
|
+
this.sessionToken = null;
|
|
185
|
+
this._sessionCartPromise = null;
|
|
186
|
+
this._migrationDone = false;
|
|
187
|
+
/** localStorage key for session cart reference (sessionToken + cartId) */
|
|
188
|
+
this.SESSION_CART_KEY = "brainerce_session";
|
|
182
189
|
/**
|
|
183
190
|
* Virtual cart ID used for localStorage carts (guest users not logged in).
|
|
184
|
-
*
|
|
191
|
+
* @deprecated Guest carts now use server-side sessions. Kept for backward compatibility.
|
|
185
192
|
*/
|
|
186
193
|
this.VIRTUAL_LOCAL_CART_ID = "__local__";
|
|
187
194
|
/**
|
|
@@ -213,6 +220,7 @@ var BrainerceClient = class {
|
|
|
213
220
|
this.baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
214
221
|
this.timeout = options.timeout || DEFAULT_TIMEOUT;
|
|
215
222
|
this.onAuthError = options.onAuthError;
|
|
223
|
+
this.hydrateSessionCart();
|
|
216
224
|
}
|
|
217
225
|
withGuards(result, type) {
|
|
218
226
|
if (!isDevGuardsEnabled()) return result;
|
|
@@ -1911,10 +1919,6 @@ var BrainerceClient = class {
|
|
|
1911
1919
|
* ```
|
|
1912
1920
|
*/
|
|
1913
1921
|
async createCart() {
|
|
1914
|
-
if (this.isVibeCodedMode() && !this.isCustomerLoggedIn()) {
|
|
1915
|
-
const localCart = this.getLocalCart();
|
|
1916
|
-
return this.localCartToCart(localCart);
|
|
1917
|
-
}
|
|
1918
1922
|
if (this.isVibeCodedMode()) {
|
|
1919
1923
|
return this.vibeCodedRequest("POST", "/cart");
|
|
1920
1924
|
}
|
|
@@ -1933,6 +1937,9 @@ var BrainerceClient = class {
|
|
|
1933
1937
|
* ```
|
|
1934
1938
|
*/
|
|
1935
1939
|
async getCartBySession(sessionToken) {
|
|
1940
|
+
if (this.isVibeCodedMode()) {
|
|
1941
|
+
return this.vibeCodedRequest("GET", `/cart/session/${sessionToken}`);
|
|
1942
|
+
}
|
|
1936
1943
|
if (this.storeId && !this.apiKey) {
|
|
1937
1944
|
return this.storefrontRequest("GET", `/cart/session/${sessionToken}`);
|
|
1938
1945
|
}
|
|
@@ -1958,6 +1965,7 @@ var BrainerceClient = class {
|
|
|
1958
1965
|
*/
|
|
1959
1966
|
async getCart(cartId) {
|
|
1960
1967
|
if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
1968
|
+
console.warn('getCart("__local__") is deprecated. Use smartGetCart() instead.');
|
|
1961
1969
|
return this.withGuards(this.localCartToCart(this.getLocalCart()), "cart");
|
|
1962
1970
|
}
|
|
1963
1971
|
if (this.isVibeCodedMode()) {
|
|
@@ -1982,38 +1990,14 @@ var BrainerceClient = class {
|
|
|
1982
1990
|
*/
|
|
1983
1991
|
async addToCart(cartId, item) {
|
|
1984
1992
|
if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
1985
|
-
|
|
1986
|
-
let productPrice = "0";
|
|
1987
|
-
let productImage;
|
|
1988
|
-
if (item.productInfo) {
|
|
1989
|
-
productName = item.productInfo.name;
|
|
1990
|
-
productPrice = item.productInfo.price;
|
|
1991
|
-
productImage = item.productInfo.image;
|
|
1992
|
-
} else {
|
|
1993
|
-
try {
|
|
1994
|
-
const product = await this.getProduct(item.productId);
|
|
1995
|
-
productName = product.name;
|
|
1996
|
-
productPrice = product.salePrice || product.basePrice;
|
|
1997
|
-
productImage = product.images?.[0]?.url;
|
|
1998
|
-
if (item.variantId && product.variants) {
|
|
1999
|
-
const variant = product.variants.find((v) => v.id === item.variantId);
|
|
2000
|
-
if (variant) {
|
|
2001
|
-
productPrice = variant.salePrice || variant.price || productPrice;
|
|
2002
|
-
if (variant.image) {
|
|
2003
|
-
productImage = typeof variant.image === "string" ? variant.image : variant.image.url;
|
|
2004
|
-
}
|
|
2005
|
-
}
|
|
2006
|
-
}
|
|
2007
|
-
} catch {
|
|
2008
|
-
}
|
|
2009
|
-
}
|
|
1993
|
+
console.warn('addToCart("__local__", ...) is deprecated. Use smartAddToCart() instead.');
|
|
2010
1994
|
this.addToLocalCart({
|
|
2011
1995
|
productId: item.productId,
|
|
2012
1996
|
variantId: item.variantId,
|
|
2013
1997
|
quantity: item.quantity,
|
|
2014
|
-
name:
|
|
2015
|
-
price:
|
|
2016
|
-
image:
|
|
1998
|
+
name: item.productInfo?.name || "Unknown Product",
|
|
1999
|
+
price: item.productInfo?.price || "0",
|
|
2000
|
+
image: item.productInfo?.image
|
|
2017
2001
|
});
|
|
2018
2002
|
return this.withGuards(this.localCartToCart(this.getLocalCart()), "cart");
|
|
2019
2003
|
}
|
|
@@ -2044,6 +2028,7 @@ var BrainerceClient = class {
|
|
|
2044
2028
|
*/
|
|
2045
2029
|
async updateCartItem(cartId, itemId, data) {
|
|
2046
2030
|
if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
2031
|
+
console.warn('updateCartItem("__local__", ...) is deprecated. Use smartUpdateCartItem() instead.');
|
|
2047
2032
|
const index = parseInt(itemId.replace("local_", ""), 10);
|
|
2048
2033
|
const localCart = this.getLocalCart();
|
|
2049
2034
|
if (index >= 0 && index < localCart.items.length) {
|
|
@@ -2079,6 +2064,7 @@ var BrainerceClient = class {
|
|
|
2079
2064
|
*/
|
|
2080
2065
|
async removeCartItem(cartId, itemId) {
|
|
2081
2066
|
if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
2067
|
+
console.warn('removeCartItem("__local__", ...) is deprecated. Use smartRemoveFromCart() instead.');
|
|
2082
2068
|
const index = parseInt(itemId.replace("local_", ""), 10);
|
|
2083
2069
|
const localCart = this.getLocalCart();
|
|
2084
2070
|
if (index >= 0 && index < localCart.items.length) {
|
|
@@ -2114,6 +2100,7 @@ var BrainerceClient = class {
|
|
|
2114
2100
|
*/
|
|
2115
2101
|
async clearCart(cartId) {
|
|
2116
2102
|
if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
2103
|
+
console.warn('clearCart("__local__") is deprecated. Use onCheckoutComplete() or clearLocalCart() directly.');
|
|
2117
2104
|
this.clearLocalCart();
|
|
2118
2105
|
return this.withGuards(this.localCartToCart(this.getLocalCart()), "cart");
|
|
2119
2106
|
}
|
|
@@ -2394,6 +2381,230 @@ var BrainerceClient = class {
|
|
|
2394
2381
|
isCustomerLoggedIn() {
|
|
2395
2382
|
return !!this.customerToken;
|
|
2396
2383
|
}
|
|
2384
|
+
// -------------------- Session Cart Helpers --------------------
|
|
2385
|
+
/**
|
|
2386
|
+
* Hydrate session cart state from localStorage on construction.
|
|
2387
|
+
* @internal
|
|
2388
|
+
*/
|
|
2389
|
+
hydrateSessionCart() {
|
|
2390
|
+
if (!this.isLocalStorageAvailable()) return;
|
|
2391
|
+
try {
|
|
2392
|
+
const stored = window.localStorage.getItem(this.SESSION_CART_KEY);
|
|
2393
|
+
if (stored) {
|
|
2394
|
+
const ref = JSON.parse(stored);
|
|
2395
|
+
this.sessionToken = ref.sessionToken;
|
|
2396
|
+
this.sessionCartId = ref.cartId;
|
|
2397
|
+
}
|
|
2398
|
+
} catch {
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
/**
|
|
2402
|
+
* Persist session cart reference to localStorage.
|
|
2403
|
+
* @internal
|
|
2404
|
+
*/
|
|
2405
|
+
saveSessionCart(ref) {
|
|
2406
|
+
this.sessionToken = ref.sessionToken;
|
|
2407
|
+
this.sessionCartId = ref.cartId;
|
|
2408
|
+
if (!this.isLocalStorageAvailable()) return;
|
|
2409
|
+
window.localStorage.setItem(this.SESSION_CART_KEY, JSON.stringify(ref));
|
|
2410
|
+
}
|
|
2411
|
+
/**
|
|
2412
|
+
* Clear session cart reference from localStorage and memory.
|
|
2413
|
+
* @internal
|
|
2414
|
+
*/
|
|
2415
|
+
clearSessionCart() {
|
|
2416
|
+
this.sessionToken = null;
|
|
2417
|
+
this.sessionCartId = null;
|
|
2418
|
+
this._sessionCartPromise = null;
|
|
2419
|
+
if (!this.isLocalStorageAvailable()) return;
|
|
2420
|
+
window.localStorage.removeItem(this.SESSION_CART_KEY);
|
|
2421
|
+
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Update the cached item count in the session reference.
|
|
2424
|
+
* @internal
|
|
2425
|
+
*/
|
|
2426
|
+
updateSessionCartItemCount(count) {
|
|
2427
|
+
if (!this.sessionToken || !this.sessionCartId) return;
|
|
2428
|
+
this.saveSessionCart({
|
|
2429
|
+
sessionToken: this.sessionToken,
|
|
2430
|
+
cartId: this.sessionCartId,
|
|
2431
|
+
itemCount: count
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
/**
|
|
2435
|
+
* Return a synthetic empty Cart object for display when no server cart exists yet.
|
|
2436
|
+
* Avoids creating a server cart just to show "your cart is empty."
|
|
2437
|
+
* @internal
|
|
2438
|
+
*/
|
|
2439
|
+
emptyCart() {
|
|
2440
|
+
return {
|
|
2441
|
+
id: "",
|
|
2442
|
+
sessionToken: null,
|
|
2443
|
+
customerId: null,
|
|
2444
|
+
status: "ACTIVE",
|
|
2445
|
+
currency: "USD",
|
|
2446
|
+
notes: null,
|
|
2447
|
+
subtotal: "0",
|
|
2448
|
+
discountAmount: "0",
|
|
2449
|
+
couponCode: null,
|
|
2450
|
+
items: [],
|
|
2451
|
+
itemCount: 0,
|
|
2452
|
+
expiresAt: null,
|
|
2453
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2454
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2455
|
+
};
|
|
2456
|
+
}
|
|
2457
|
+
/**
|
|
2458
|
+
* Create a server-side cart via API. Never falls back to localStorage.
|
|
2459
|
+
* @internal
|
|
2460
|
+
*/
|
|
2461
|
+
async createServerCart() {
|
|
2462
|
+
if (this.isVibeCodedMode()) {
|
|
2463
|
+
return this.vibeCodedRequest("POST", "/cart");
|
|
2464
|
+
}
|
|
2465
|
+
if (this.storeId && !this.apiKey) {
|
|
2466
|
+
return this.storefrontRequest("POST", "/cart");
|
|
2467
|
+
}
|
|
2468
|
+
return this.adminRequest("POST", "/api/v1/cart");
|
|
2469
|
+
}
|
|
2470
|
+
/**
|
|
2471
|
+
* Get or create a server-side session cart for guest users.
|
|
2472
|
+
* Lazily creates the cart on first call (e.g., first add-to-cart).
|
|
2473
|
+
* Uses promise dedup lock to prevent race conditions on parallel calls.
|
|
2474
|
+
* @internal
|
|
2475
|
+
*/
|
|
2476
|
+
async getOrCreateSessionCart() {
|
|
2477
|
+
if (this._sessionCartPromise) return this._sessionCartPromise;
|
|
2478
|
+
this._sessionCartPromise = this._getOrCreateSessionCartImpl();
|
|
2479
|
+
try {
|
|
2480
|
+
return await this._sessionCartPromise;
|
|
2481
|
+
} finally {
|
|
2482
|
+
this._sessionCartPromise = null;
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
/** @internal */
|
|
2486
|
+
async _getOrCreateSessionCartImpl() {
|
|
2487
|
+
const migrated = await this.migrateLocalCartToSession();
|
|
2488
|
+
if (migrated) return migrated;
|
|
2489
|
+
if (this.sessionCartId && this.sessionToken) {
|
|
2490
|
+
try {
|
|
2491
|
+
const cart2 = await this.getCart(this.sessionCartId);
|
|
2492
|
+
if (cart2.status === "ACTIVE") {
|
|
2493
|
+
return cart2;
|
|
2494
|
+
}
|
|
2495
|
+
this.clearSessionCart();
|
|
2496
|
+
} catch {
|
|
2497
|
+
this.clearSessionCart();
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
if (this.sessionToken) {
|
|
2501
|
+
try {
|
|
2502
|
+
const cart2 = await this.getCartBySession(this.sessionToken);
|
|
2503
|
+
if (cart2 && cart2.status === "ACTIVE") {
|
|
2504
|
+
this.saveSessionCart({
|
|
2505
|
+
sessionToken: this.sessionToken,
|
|
2506
|
+
cartId: cart2.id,
|
|
2507
|
+
itemCount: cart2.itemCount
|
|
2508
|
+
});
|
|
2509
|
+
return cart2;
|
|
2510
|
+
}
|
|
2511
|
+
} catch {
|
|
2512
|
+
this.clearSessionCart();
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
const cart = await this.createServerCart();
|
|
2516
|
+
if (cart.sessionToken) {
|
|
2517
|
+
this.saveSessionCart({
|
|
2518
|
+
sessionToken: cart.sessionToken,
|
|
2519
|
+
cartId: cart.id,
|
|
2520
|
+
itemCount: cart.itemCount
|
|
2521
|
+
});
|
|
2522
|
+
}
|
|
2523
|
+
return cart;
|
|
2524
|
+
}
|
|
2525
|
+
/**
|
|
2526
|
+
* Migrate legacy localStorage cart to server session cart.
|
|
2527
|
+
* Called once, on first smart method call if migration is needed.
|
|
2528
|
+
* @internal
|
|
2529
|
+
*/
|
|
2530
|
+
async migrateLocalCartToSession() {
|
|
2531
|
+
if (this._migrationDone) return null;
|
|
2532
|
+
this._migrationDone = true;
|
|
2533
|
+
if (this.sessionToken || this.sessionCartId) return null;
|
|
2534
|
+
const localCart = this.getLocalCart();
|
|
2535
|
+
if (localCart.items.length === 0) return null;
|
|
2536
|
+
try {
|
|
2537
|
+
const cart = await this.createServerCart();
|
|
2538
|
+
for (const item of localCart.items) {
|
|
2539
|
+
try {
|
|
2540
|
+
await this.addToCart(cart.id, {
|
|
2541
|
+
productId: item.productId,
|
|
2542
|
+
variantId: item.variantId,
|
|
2543
|
+
quantity: item.quantity
|
|
2544
|
+
});
|
|
2545
|
+
} catch {
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
if (cart.sessionToken) {
|
|
2549
|
+
this.saveSessionCart({
|
|
2550
|
+
sessionToken: cart.sessionToken,
|
|
2551
|
+
cartId: cart.id
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2554
|
+
this.clearLocalCart();
|
|
2555
|
+
return this.getCart(cart.id);
|
|
2556
|
+
} catch {
|
|
2557
|
+
this._migrationDone = false;
|
|
2558
|
+
return null;
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
/**
|
|
2562
|
+
* Merge the guest session cart into the customer's cart on the server.
|
|
2563
|
+
* Tries linkCart first (fast, single call), falls back to mergeCarts endpoint.
|
|
2564
|
+
* @internal
|
|
2565
|
+
*/
|
|
2566
|
+
async mergeSessionCartOnLogin() {
|
|
2567
|
+
if (this.sessionCartId) {
|
|
2568
|
+
try {
|
|
2569
|
+
return await this.linkCart(this.sessionCartId);
|
|
2570
|
+
} catch {
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
if (this.isVibeCodedMode()) {
|
|
2574
|
+
return this.vibeCodedRequest("POST", "/cart/merge", {
|
|
2575
|
+
sessionToken: this.sessionToken
|
|
2576
|
+
});
|
|
2577
|
+
}
|
|
2578
|
+
if (this.storeId && !this.apiKey) {
|
|
2579
|
+
return this.storefrontRequest("POST", "/cart/merge", {
|
|
2580
|
+
sessionToken: this.sessionToken
|
|
2581
|
+
});
|
|
2582
|
+
}
|
|
2583
|
+
return this.adminRequest("POST", "/api/v1/cart/merge", {
|
|
2584
|
+
sessionToken: this.sessionToken
|
|
2585
|
+
});
|
|
2586
|
+
}
|
|
2587
|
+
/**
|
|
2588
|
+
* Get the cart item count without a full cart fetch.
|
|
2589
|
+
* Returns the cached count from localStorage if available, or 0.
|
|
2590
|
+
* For accurate counts, use smartGetCart() and read cart.itemCount.
|
|
2591
|
+
*/
|
|
2592
|
+
getSmartCartItemCount() {
|
|
2593
|
+
if (this.isCustomerLoggedIn()) {
|
|
2594
|
+
return 0;
|
|
2595
|
+
}
|
|
2596
|
+
if (!this.isLocalStorageAvailable()) return 0;
|
|
2597
|
+
try {
|
|
2598
|
+
const stored = window.localStorage.getItem(this.SESSION_CART_KEY);
|
|
2599
|
+
if (stored) {
|
|
2600
|
+
const ref = JSON.parse(stored);
|
|
2601
|
+
return ref.itemCount ?? 0;
|
|
2602
|
+
}
|
|
2603
|
+
} catch {
|
|
2604
|
+
}
|
|
2605
|
+
return 0;
|
|
2606
|
+
}
|
|
2607
|
+
// -------------------- Customer Cart --------------------
|
|
2397
2608
|
/**
|
|
2398
2609
|
* Get or create a server cart for the logged-in customer
|
|
2399
2610
|
* Caches the cart ID for subsequent calls
|
|
@@ -2446,10 +2657,10 @@ var BrainerceClient = class {
|
|
|
2446
2657
|
throw new BrainerceError("fetchCustomerCart requires vibe-coded or storefront mode", 400);
|
|
2447
2658
|
}
|
|
2448
2659
|
/**
|
|
2449
|
-
* Smart add to cart - automatically uses
|
|
2660
|
+
* Smart add to cart - automatically uses the correct cart based on auth state
|
|
2450
2661
|
*
|
|
2451
|
-
* - **
|
|
2452
|
-
* - **
|
|
2662
|
+
* - **Logged in**: Uses customer's server cart
|
|
2663
|
+
* - **Guest**: Uses server-side session cart (creates one if needed)
|
|
2453
2664
|
*
|
|
2454
2665
|
* @example
|
|
2455
2666
|
* ```typescript
|
|
@@ -2457,25 +2668,35 @@ var BrainerceClient = class {
|
|
|
2457
2668
|
* await client.smartAddToCart({
|
|
2458
2669
|
* productId: 'prod_123',
|
|
2459
2670
|
* quantity: 2,
|
|
2460
|
-
* name: 'Cool Product', // Optional: for localStorage display
|
|
2461
|
-
* price: '29.99',
|
|
2462
2671
|
* });
|
|
2463
2672
|
* ```
|
|
2464
2673
|
*/
|
|
2465
2674
|
async smartAddToCart(item) {
|
|
2466
2675
|
if (this.isCustomerLoggedIn()) {
|
|
2467
2676
|
const cart = await this.getOrCreateCustomerCart();
|
|
2468
|
-
|
|
2677
|
+
const updated = await this.addToCart(cart.id, {
|
|
2469
2678
|
productId: item.productId,
|
|
2470
2679
|
variantId: item.variantId,
|
|
2471
2680
|
quantity: item.quantity
|
|
2472
2681
|
});
|
|
2682
|
+
return updated;
|
|
2473
2683
|
} else {
|
|
2474
|
-
|
|
2684
|
+
const cart = await this.getOrCreateSessionCart();
|
|
2685
|
+
const updated = await this.addToCart(cart.id, {
|
|
2686
|
+
productId: item.productId,
|
|
2687
|
+
variantId: item.variantId,
|
|
2688
|
+
quantity: item.quantity
|
|
2689
|
+
});
|
|
2690
|
+
this.updateSessionCartItemCount(updated.items?.length ?? 0);
|
|
2691
|
+
return updated;
|
|
2475
2692
|
}
|
|
2476
2693
|
}
|
|
2477
2694
|
/**
|
|
2478
|
-
* Smart get cart - returns
|
|
2695
|
+
* Smart get cart - returns the current cart (server-side for both guests and logged-in users)
|
|
2696
|
+
*
|
|
2697
|
+
* - **Logged in**: Returns customer's server cart
|
|
2698
|
+
* - **Guest with session**: Returns server-side session cart
|
|
2699
|
+
* - **Guest without session**: Returns empty cart (no server call, cart created lazily on add)
|
|
2479
2700
|
*
|
|
2480
2701
|
* @example
|
|
2481
2702
|
* ```typescript
|
|
@@ -2487,7 +2708,10 @@ var BrainerceClient = class {
|
|
|
2487
2708
|
if (this.isCustomerLoggedIn()) {
|
|
2488
2709
|
return this.getOrCreateCustomerCart();
|
|
2489
2710
|
} else {
|
|
2490
|
-
|
|
2711
|
+
if (!this.sessionToken) {
|
|
2712
|
+
return this.emptyCart();
|
|
2713
|
+
}
|
|
2714
|
+
return this.getOrCreateSessionCart();
|
|
2491
2715
|
}
|
|
2492
2716
|
}
|
|
2493
2717
|
/**
|
|
@@ -2513,7 +2737,24 @@ var BrainerceClient = class {
|
|
|
2513
2737
|
}
|
|
2514
2738
|
return this.updateCartItem(cart.id, item.id, { quantity });
|
|
2515
2739
|
} else {
|
|
2516
|
-
|
|
2740
|
+
if (!this.sessionToken) {
|
|
2741
|
+
return this.emptyCart();
|
|
2742
|
+
}
|
|
2743
|
+
const cart = await this.getOrCreateSessionCart();
|
|
2744
|
+
const item = cart.items.find(
|
|
2745
|
+
(i) => i.productId === productId && i.variantId === (variantId ?? null)
|
|
2746
|
+
);
|
|
2747
|
+
if (!item) {
|
|
2748
|
+
return cart;
|
|
2749
|
+
}
|
|
2750
|
+
let updated;
|
|
2751
|
+
if (quantity <= 0) {
|
|
2752
|
+
updated = await this.removeCartItem(cart.id, item.id);
|
|
2753
|
+
} else {
|
|
2754
|
+
updated = await this.updateCartItem(cart.id, item.id, { quantity });
|
|
2755
|
+
}
|
|
2756
|
+
this.updateSessionCartItemCount(updated.items?.length ?? 0);
|
|
2757
|
+
return updated;
|
|
2517
2758
|
}
|
|
2518
2759
|
}
|
|
2519
2760
|
/**
|
|
@@ -2529,15 +2770,13 @@ var BrainerceClient = class {
|
|
|
2529
2770
|
return this.smartUpdateCartItem(productId, 0, variantId);
|
|
2530
2771
|
}
|
|
2531
2772
|
/**
|
|
2532
|
-
* Sync
|
|
2773
|
+
* Sync guest cart to customer cart on login
|
|
2533
2774
|
*
|
|
2534
2775
|
* Call this AFTER setCustomerToken() when a customer logs in.
|
|
2535
2776
|
* This will:
|
|
2536
|
-
* 1.
|
|
2537
|
-
* 2.
|
|
2538
|
-
* 3. Clear localStorage
|
|
2539
|
-
*
|
|
2540
|
-
* Items that fail to sync (e.g., out of stock) are silently skipped.
|
|
2777
|
+
* 1. If a server-side session cart exists, merge it into the customer cart (atomic, server-side)
|
|
2778
|
+
* 2. If only a legacy localStorage cart exists, sync items one by one (backward compat)
|
|
2779
|
+
* 3. Clear the session/localStorage after merge
|
|
2541
2780
|
*
|
|
2542
2781
|
* @example
|
|
2543
2782
|
* ```typescript
|
|
@@ -2545,7 +2784,7 @@ var BrainerceClient = class {
|
|
|
2545
2784
|
* const auth = await client.login(email, password);
|
|
2546
2785
|
* client.setCustomerToken(auth.token);
|
|
2547
2786
|
*
|
|
2548
|
-
* //
|
|
2787
|
+
* // Merge their guest cart into customer cart
|
|
2549
2788
|
* const cart = await client.syncCartOnLogin();
|
|
2550
2789
|
* console.log('Cart synced, items:', cart.items.length);
|
|
2551
2790
|
* ```
|
|
@@ -2557,6 +2796,14 @@ var BrainerceClient = class {
|
|
|
2557
2796
|
401
|
|
2558
2797
|
);
|
|
2559
2798
|
}
|
|
2799
|
+
if (this.sessionToken) {
|
|
2800
|
+
try {
|
|
2801
|
+
const merged = await this.mergeSessionCartOnLogin();
|
|
2802
|
+
this.clearSessionCart();
|
|
2803
|
+
return merged;
|
|
2804
|
+
} catch {
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2560
2807
|
const localCart = this.getLocalCart();
|
|
2561
2808
|
const serverCart = await this.getOrCreateCustomerCart();
|
|
2562
2809
|
if (localCart.items.length === 0) {
|
|
@@ -2578,15 +2825,15 @@ var BrainerceClient = class {
|
|
|
2578
2825
|
/**
|
|
2579
2826
|
* Clear cart state on logout
|
|
2580
2827
|
*
|
|
2581
|
-
* Call this when a customer logs out to clear the cached cart ID.
|
|
2582
|
-
* The
|
|
2828
|
+
* Call this when a customer logs out to clear the cached customer cart ID.
|
|
2829
|
+
* The session cart (if any) remains available for the next guest session.
|
|
2583
2830
|
*
|
|
2584
2831
|
* @example
|
|
2585
2832
|
* ```typescript
|
|
2586
2833
|
* client.clearCustomerToken();
|
|
2587
2834
|
* client.onLogout();
|
|
2588
2835
|
*
|
|
2589
|
-
* // Now back to guest mode - cart uses
|
|
2836
|
+
* // Now back to guest mode - cart uses server-side session
|
|
2590
2837
|
* await client.smartAddToCart({ productId: 'prod_123', quantity: 1 });
|
|
2591
2838
|
* ```
|
|
2592
2839
|
*/
|
|
@@ -2604,12 +2851,12 @@ var BrainerceClient = class {
|
|
|
2604
2851
|
* // After payment success
|
|
2605
2852
|
* if (paymentStatus === 'succeeded') {
|
|
2606
2853
|
* client.onCheckoutComplete();
|
|
2607
|
-
* client.clearLocalCart(); // Also clear localStorage for guests
|
|
2608
2854
|
* }
|
|
2609
2855
|
* ```
|
|
2610
2856
|
*/
|
|
2611
2857
|
onCheckoutComplete() {
|
|
2612
2858
|
this.customerCartId = null;
|
|
2859
|
+
this.clearSessionCart();
|
|
2613
2860
|
}
|
|
2614
2861
|
// -------------------- Checkout --------------------
|
|
2615
2862
|
/**
|
|
@@ -2636,7 +2883,7 @@ var BrainerceClient = class {
|
|
|
2636
2883
|
async createCheckout(data) {
|
|
2637
2884
|
if (data.cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
2638
2885
|
throw new BrainerceError(
|
|
2639
|
-
|
|
2886
|
+
'Cannot create checkout from "__local__" cart. Use startGuestCheckout() or smartAddToCart() to create a server-side cart first.',
|
|
2640
2887
|
400
|
|
2641
2888
|
);
|
|
2642
2889
|
}
|
|
@@ -3172,14 +3419,8 @@ var BrainerceClient = class {
|
|
|
3172
3419
|
}
|
|
3173
3420
|
}
|
|
3174
3421
|
/**
|
|
3175
|
-
*
|
|
3176
|
-
* Returns empty cart if none exists
|
|
3177
|
-
*
|
|
3178
|
-
* @example
|
|
3179
|
-
* ```typescript
|
|
3180
|
-
* const cart = client.getLocalCart();
|
|
3181
|
-
* console.log('Items in cart:', cart.items.length);
|
|
3182
|
-
* ```
|
|
3422
|
+
* @deprecated Use `smartGetCart()` instead. Guest carts are now stored on the server.
|
|
3423
|
+
* Get local cart from localStorage. Returns empty cart if none exists.
|
|
3183
3424
|
*/
|
|
3184
3425
|
getLocalCart() {
|
|
3185
3426
|
if (!this.isLocalStorageAvailable()) {
|
|
@@ -3261,18 +3502,8 @@ var BrainerceClient = class {
|
|
|
3261
3502
|
};
|
|
3262
3503
|
}
|
|
3263
3504
|
/**
|
|
3264
|
-
*
|
|
3265
|
-
* If item already exists, updates quantity
|
|
3266
|
-
*
|
|
3267
|
-
* @example
|
|
3268
|
-
* ```typescript
|
|
3269
|
-
* client.addToLocalCart({
|
|
3270
|
-
* productId: 'prod_123',
|
|
3271
|
-
* quantity: 2,
|
|
3272
|
-
* name: 'Cool Shirt',
|
|
3273
|
-
* price: '29.99',
|
|
3274
|
-
* });
|
|
3275
|
-
* ```
|
|
3505
|
+
* @deprecated Use `smartAddToCart()` instead. Guest carts are now stored on the server.
|
|
3506
|
+
* Add item to local cart (NO API call). If item already exists, updates quantity.
|
|
3276
3507
|
*/
|
|
3277
3508
|
addToLocalCart(item) {
|
|
3278
3509
|
const cart = this.getLocalCart();
|
|
@@ -3294,14 +3525,8 @@ var BrainerceClient = class {
|
|
|
3294
3525
|
return cart;
|
|
3295
3526
|
}
|
|
3296
3527
|
/**
|
|
3297
|
-
*
|
|
3298
|
-
* Set quantity to 0 to remove item
|
|
3299
|
-
*
|
|
3300
|
-
* @example
|
|
3301
|
-
* ```typescript
|
|
3302
|
-
* client.updateLocalCartItem('prod_123', 3); // Set quantity to 3
|
|
3303
|
-
* client.updateLocalCartItem('prod_123', 0); // Remove item
|
|
3304
|
-
* ```
|
|
3528
|
+
* @deprecated Use `smartUpdateCartItem()` instead. Guest carts are now stored on the server.
|
|
3529
|
+
* Update item quantity in local cart. Set quantity to 0 to remove item.
|
|
3305
3530
|
*/
|
|
3306
3531
|
updateLocalCartItem(productId, quantity, variantId) {
|
|
3307
3532
|
const cart = this.getLocalCart();
|
|
@@ -3319,24 +3544,13 @@ var BrainerceClient = class {
|
|
|
3319
3544
|
return cart;
|
|
3320
3545
|
}
|
|
3321
3546
|
/**
|
|
3322
|
-
*
|
|
3323
|
-
*
|
|
3324
|
-
* @example
|
|
3325
|
-
* ```typescript
|
|
3326
|
-
* client.removeFromLocalCart('prod_123');
|
|
3327
|
-
* client.removeFromLocalCart('prod_456', 'variant_789');
|
|
3328
|
-
* ```
|
|
3547
|
+
* @deprecated Use `smartRemoveFromCart()` instead. Guest carts are now stored on the server.
|
|
3329
3548
|
*/
|
|
3330
3549
|
removeFromLocalCart(productId, variantId) {
|
|
3331
3550
|
return this.updateLocalCartItem(productId, 0, variantId);
|
|
3332
3551
|
}
|
|
3333
3552
|
/**
|
|
3334
|
-
*
|
|
3335
|
-
*
|
|
3336
|
-
* @example
|
|
3337
|
-
* ```typescript
|
|
3338
|
-
* client.clearLocalCart();
|
|
3339
|
-
* ```
|
|
3553
|
+
* @deprecated Use `onCheckoutComplete()` instead. Guest carts are now stored on the server.
|
|
3340
3554
|
*/
|
|
3341
3555
|
clearLocalCart() {
|
|
3342
3556
|
const cart = { items: [], updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -3344,16 +3558,8 @@ var BrainerceClient = class {
|
|
|
3344
3558
|
return cart;
|
|
3345
3559
|
}
|
|
3346
3560
|
/**
|
|
3347
|
-
*
|
|
3348
|
-
*
|
|
3349
|
-
* @example
|
|
3350
|
-
* ```typescript
|
|
3351
|
-
* client.setLocalCartCustomer({
|
|
3352
|
-
* email: 'john@example.com',
|
|
3353
|
-
* firstName: 'John',
|
|
3354
|
-
* lastName: 'Doe',
|
|
3355
|
-
* });
|
|
3356
|
-
* ```
|
|
3561
|
+
* @deprecated Customer info is now set via checkout address methods.
|
|
3562
|
+
* Still used by `submitGuestOrder()` for backward compatibility.
|
|
3357
3563
|
*/
|
|
3358
3564
|
setLocalCartCustomer(customer) {
|
|
3359
3565
|
const cart = this.getLocalCart();
|
|
@@ -3362,19 +3568,8 @@ var BrainerceClient = class {
|
|
|
3362
3568
|
return cart;
|
|
3363
3569
|
}
|
|
3364
3570
|
/**
|
|
3365
|
-
*
|
|
3366
|
-
*
|
|
3367
|
-
* @example
|
|
3368
|
-
* ```typescript
|
|
3369
|
-
* client.setLocalCartShippingAddress({
|
|
3370
|
-
* firstName: 'John',
|
|
3371
|
-
* lastName: 'Doe',
|
|
3372
|
-
* line1: '123 Main St',
|
|
3373
|
-
* city: 'Tel Aviv',
|
|
3374
|
-
* postalCode: '6100000',
|
|
3375
|
-
* country: 'IL',
|
|
3376
|
-
* });
|
|
3377
|
-
* ```
|
|
3571
|
+
* @deprecated Address is now set via `updateGuestCheckoutAddress()` after `startGuestCheckout()`.
|
|
3572
|
+
* Still used by `submitGuestOrder()` for backward compatibility.
|
|
3378
3573
|
*/
|
|
3379
3574
|
setLocalCartShippingAddress(address) {
|
|
3380
3575
|
const cart = this.getLocalCart();
|
|
@@ -3383,7 +3578,8 @@ var BrainerceClient = class {
|
|
|
3383
3578
|
return cart;
|
|
3384
3579
|
}
|
|
3385
3580
|
/**
|
|
3386
|
-
*
|
|
3581
|
+
* @deprecated Address is now set via `updateGuestCheckoutAddress()` after `startGuestCheckout()`.
|
|
3582
|
+
* Still used by `submitGuestOrder()` for backward compatibility.
|
|
3387
3583
|
*/
|
|
3388
3584
|
setLocalCartBillingAddress(address) {
|
|
3389
3585
|
const cart = this.getLocalCart();
|
|
@@ -3392,7 +3588,7 @@ var BrainerceClient = class {
|
|
|
3392
3588
|
return cart;
|
|
3393
3589
|
}
|
|
3394
3590
|
/**
|
|
3395
|
-
*
|
|
3591
|
+
* @deprecated Coupons are now applied via `applyCoupon()` on the server cart.
|
|
3396
3592
|
*/
|
|
3397
3593
|
setLocalCartCoupon(couponCode) {
|
|
3398
3594
|
const cart = this.getLocalCart();
|
|
@@ -3401,7 +3597,7 @@ var BrainerceClient = class {
|
|
|
3401
3597
|
return cart;
|
|
3402
3598
|
}
|
|
3403
3599
|
/**
|
|
3404
|
-
*
|
|
3600
|
+
* @deprecated Use `getSmartCartItemCount()` instead.
|
|
3405
3601
|
*/
|
|
3406
3602
|
getLocalCartItemCount() {
|
|
3407
3603
|
const cart = this.getLocalCart();
|
|
@@ -3431,6 +3627,40 @@ var BrainerceClient = class {
|
|
|
3431
3627
|
* ```
|
|
3432
3628
|
*/
|
|
3433
3629
|
async submitGuestOrder(options) {
|
|
3630
|
+
if (this.sessionToken) {
|
|
3631
|
+
const serverCart = await this.getOrCreateSessionCart();
|
|
3632
|
+
if (!serverCart.items || serverCart.items.length === 0) {
|
|
3633
|
+
throw new BrainerceError("Cart is empty", 400);
|
|
3634
|
+
}
|
|
3635
|
+
const localCart = this.getLocalCart();
|
|
3636
|
+
if (!localCart.customer?.email) {
|
|
3637
|
+
throw new BrainerceError("Customer email is required. Call setLocalCartCustomer() first.", 400);
|
|
3638
|
+
}
|
|
3639
|
+
if (!localCart.shippingAddress) {
|
|
3640
|
+
throw new BrainerceError("Shipping address is required. Call setLocalCartShippingAddress() first.", 400);
|
|
3641
|
+
}
|
|
3642
|
+
const trackingResult = await this.startGuestCheckout();
|
|
3643
|
+
if (!trackingResult.tracked) {
|
|
3644
|
+
throw new BrainerceError("Failed to create checkout from session cart", 500);
|
|
3645
|
+
}
|
|
3646
|
+
await this.updateGuestCheckoutAddress(trackingResult.checkoutId, {
|
|
3647
|
+
shippingAddress: {
|
|
3648
|
+
...localCart.shippingAddress,
|
|
3649
|
+
email: localCart.customer.email
|
|
3650
|
+
},
|
|
3651
|
+
billingAddress: localCart.billingAddress
|
|
3652
|
+
});
|
|
3653
|
+
const orderResult = await this.completeGuestCheckout(trackingResult.checkoutId, {
|
|
3654
|
+
clearCartOnSuccess: options?.clearCartOnSuccess
|
|
3655
|
+
});
|
|
3656
|
+
return {
|
|
3657
|
+
orderId: orderResult.orderId,
|
|
3658
|
+
orderNumber: orderResult.orderNumber || orderResult.orderId,
|
|
3659
|
+
status: orderResult.status || "pending",
|
|
3660
|
+
total: orderResult.total ?? 0,
|
|
3661
|
+
message: orderResult.message || "Order created successfully"
|
|
3662
|
+
};
|
|
3663
|
+
}
|
|
3434
3664
|
const cart = this.getLocalCart();
|
|
3435
3665
|
if (cart.items.length === 0) {
|
|
3436
3666
|
throw new BrainerceError("Cart is empty", 400);
|
|
@@ -3448,7 +3678,6 @@ var BrainerceClient = class {
|
|
|
3448
3678
|
shippingAddress: {
|
|
3449
3679
|
...cart.shippingAddress,
|
|
3450
3680
|
email: cart.customer.email
|
|
3451
|
-
// Already validated above
|
|
3452
3681
|
},
|
|
3453
3682
|
billingAddress: cart.billingAddress
|
|
3454
3683
|
});
|
|
@@ -3536,6 +3765,34 @@ var BrainerceClient = class {
|
|
|
3536
3765
|
* ```
|
|
3537
3766
|
*/
|
|
3538
3767
|
async startGuestCheckout(options) {
|
|
3768
|
+
if (this.sessionToken) {
|
|
3769
|
+
const serverCart = await this.getOrCreateSessionCart();
|
|
3770
|
+
if (!serverCart.items || serverCart.items.length === 0) {
|
|
3771
|
+
throw new BrainerceError("Cart is empty", 400);
|
|
3772
|
+
}
|
|
3773
|
+
let selectedItemIds;
|
|
3774
|
+
if (options?.selectedIndices?.length) {
|
|
3775
|
+
selectedItemIds = options.selectedIndices.map((i) => serverCart.items[i]?.id).filter((id) => !!id);
|
|
3776
|
+
if (selectedItemIds.length === 0) {
|
|
3777
|
+
throw new BrainerceError("No items selected for checkout", 400);
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
const checkout = await this.createCheckout({
|
|
3781
|
+
cartId: serverCart.id,
|
|
3782
|
+
selectedItemIds
|
|
3783
|
+
});
|
|
3784
|
+
this.saveActiveCheckout({
|
|
3785
|
+
checkoutId: checkout.id,
|
|
3786
|
+
cartId: serverCart.id,
|
|
3787
|
+
selectedIndices: options?.selectedIndices
|
|
3788
|
+
});
|
|
3789
|
+
return {
|
|
3790
|
+
tracked: true,
|
|
3791
|
+
checkoutId: checkout.id,
|
|
3792
|
+
cartId: serverCart.id,
|
|
3793
|
+
message: "Checkout created from session cart"
|
|
3794
|
+
};
|
|
3795
|
+
}
|
|
3539
3796
|
const cart = this.getLocalCart();
|
|
3540
3797
|
if (cart.items.length === 0) {
|
|
3541
3798
|
throw new BrainerceError("Cart is empty", 400);
|
|
@@ -3599,16 +3856,8 @@ var BrainerceClient = class {
|
|
|
3599
3856
|
}
|
|
3600
3857
|
}
|
|
3601
3858
|
/**
|
|
3859
|
+
* @deprecated Partial checkout is now handled server-side via `selectedItemIds`.
|
|
3602
3860
|
* Remove specific items from local cart by their indices.
|
|
3603
|
-
* Use after partial checkout to remove only the purchased items.
|
|
3604
|
-
*
|
|
3605
|
-
* @param indices - Array of item indices to remove
|
|
3606
|
-
*
|
|
3607
|
-
* @example
|
|
3608
|
-
* ```typescript
|
|
3609
|
-
* // After partial checkout success, remove purchased items
|
|
3610
|
-
* client.removeLocalCartItemsByIndex([0, 2]); // Removes items at index 0 and 2
|
|
3611
|
-
* ```
|
|
3612
3861
|
*/
|
|
3613
3862
|
removeLocalCartItemsByIndex(indices) {
|
|
3614
3863
|
const cart = this.getLocalCart();
|
|
@@ -3685,6 +3934,7 @@ var BrainerceClient = class {
|
|
|
3685
3934
|
this.removeLocalCartItemsByIndex(indices);
|
|
3686
3935
|
} else {
|
|
3687
3936
|
this.clearLocalCart();
|
|
3937
|
+
this.clearSessionCart();
|
|
3688
3938
|
}
|
|
3689
3939
|
this.customerCartId = null;
|
|
3690
3940
|
}
|
|
@@ -3782,6 +4032,7 @@ var BrainerceClient = class {
|
|
|
3782
4032
|
}
|
|
3783
4033
|
if (isLoggedIn) {
|
|
3784
4034
|
this.clearLocalCart();
|
|
4035
|
+
this.clearSessionCart();
|
|
3785
4036
|
return {
|
|
3786
4037
|
cleared: true,
|
|
3787
4038
|
mode: "full",
|
|
@@ -3798,6 +4049,7 @@ var BrainerceClient = class {
|
|
|
3798
4049
|
};
|
|
3799
4050
|
}
|
|
3800
4051
|
this.clearLocalCart();
|
|
4052
|
+
this.clearSessionCart();
|
|
3801
4053
|
return {
|
|
3802
4054
|
cleared: true,
|
|
3803
4055
|
mode: "full",
|