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.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,9 @@ var BrainerceClient = class {
|
|
|
2044
2028
|
*/
|
|
2045
2029
|
async updateCartItem(cartId, itemId, data) {
|
|
2046
2030
|
if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
2031
|
+
console.warn(
|
|
2032
|
+
'updateCartItem("__local__", ...) is deprecated. Use smartUpdateCartItem() instead.'
|
|
2033
|
+
);
|
|
2047
2034
|
const index = parseInt(itemId.replace("local_", ""), 10);
|
|
2048
2035
|
const localCart = this.getLocalCart();
|
|
2049
2036
|
if (index >= 0 && index < localCart.items.length) {
|
|
@@ -2079,6 +2066,9 @@ var BrainerceClient = class {
|
|
|
2079
2066
|
*/
|
|
2080
2067
|
async removeCartItem(cartId, itemId) {
|
|
2081
2068
|
if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
2069
|
+
console.warn(
|
|
2070
|
+
'removeCartItem("__local__", ...) is deprecated. Use smartRemoveFromCart() instead.'
|
|
2071
|
+
);
|
|
2082
2072
|
const index = parseInt(itemId.replace("local_", ""), 10);
|
|
2083
2073
|
const localCart = this.getLocalCart();
|
|
2084
2074
|
if (index >= 0 && index < localCart.items.length) {
|
|
@@ -2114,6 +2104,9 @@ var BrainerceClient = class {
|
|
|
2114
2104
|
*/
|
|
2115
2105
|
async clearCart(cartId) {
|
|
2116
2106
|
if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
2107
|
+
console.warn(
|
|
2108
|
+
'clearCart("__local__") is deprecated. Use onCheckoutComplete() or clearLocalCart() directly.'
|
|
2109
|
+
);
|
|
2117
2110
|
this.clearLocalCart();
|
|
2118
2111
|
return this.withGuards(this.localCartToCart(this.getLocalCart()), "cart");
|
|
2119
2112
|
}
|
|
@@ -2394,6 +2387,230 @@ var BrainerceClient = class {
|
|
|
2394
2387
|
isCustomerLoggedIn() {
|
|
2395
2388
|
return !!this.customerToken;
|
|
2396
2389
|
}
|
|
2390
|
+
// -------------------- Session Cart Helpers --------------------
|
|
2391
|
+
/**
|
|
2392
|
+
* Hydrate session cart state from localStorage on construction.
|
|
2393
|
+
* @internal
|
|
2394
|
+
*/
|
|
2395
|
+
hydrateSessionCart() {
|
|
2396
|
+
if (!this.isLocalStorageAvailable()) return;
|
|
2397
|
+
try {
|
|
2398
|
+
const stored = window.localStorage.getItem(this.SESSION_CART_KEY);
|
|
2399
|
+
if (stored) {
|
|
2400
|
+
const ref = JSON.parse(stored);
|
|
2401
|
+
this.sessionToken = ref.sessionToken;
|
|
2402
|
+
this.sessionCartId = ref.cartId;
|
|
2403
|
+
}
|
|
2404
|
+
} catch {
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
/**
|
|
2408
|
+
* Persist session cart reference to localStorage.
|
|
2409
|
+
* @internal
|
|
2410
|
+
*/
|
|
2411
|
+
saveSessionCart(ref) {
|
|
2412
|
+
this.sessionToken = ref.sessionToken;
|
|
2413
|
+
this.sessionCartId = ref.cartId;
|
|
2414
|
+
if (!this.isLocalStorageAvailable()) return;
|
|
2415
|
+
window.localStorage.setItem(this.SESSION_CART_KEY, JSON.stringify(ref));
|
|
2416
|
+
}
|
|
2417
|
+
/**
|
|
2418
|
+
* Clear session cart reference from localStorage and memory.
|
|
2419
|
+
* @internal
|
|
2420
|
+
*/
|
|
2421
|
+
clearSessionCart() {
|
|
2422
|
+
this.sessionToken = null;
|
|
2423
|
+
this.sessionCartId = null;
|
|
2424
|
+
this._sessionCartPromise = null;
|
|
2425
|
+
if (!this.isLocalStorageAvailable()) return;
|
|
2426
|
+
window.localStorage.removeItem(this.SESSION_CART_KEY);
|
|
2427
|
+
}
|
|
2428
|
+
/**
|
|
2429
|
+
* Update the cached item count in the session reference.
|
|
2430
|
+
* @internal
|
|
2431
|
+
*/
|
|
2432
|
+
updateSessionCartItemCount(count) {
|
|
2433
|
+
if (!this.sessionToken || !this.sessionCartId) return;
|
|
2434
|
+
this.saveSessionCart({
|
|
2435
|
+
sessionToken: this.sessionToken,
|
|
2436
|
+
cartId: this.sessionCartId,
|
|
2437
|
+
itemCount: count
|
|
2438
|
+
});
|
|
2439
|
+
}
|
|
2440
|
+
/**
|
|
2441
|
+
* Return a synthetic empty Cart object for display when no server cart exists yet.
|
|
2442
|
+
* Avoids creating a server cart just to show "your cart is empty."
|
|
2443
|
+
* @internal
|
|
2444
|
+
*/
|
|
2445
|
+
emptyCart() {
|
|
2446
|
+
return {
|
|
2447
|
+
id: "",
|
|
2448
|
+
sessionToken: null,
|
|
2449
|
+
customerId: null,
|
|
2450
|
+
status: "ACTIVE",
|
|
2451
|
+
currency: "USD",
|
|
2452
|
+
notes: null,
|
|
2453
|
+
subtotal: "0",
|
|
2454
|
+
discountAmount: "0",
|
|
2455
|
+
couponCode: null,
|
|
2456
|
+
items: [],
|
|
2457
|
+
itemCount: 0,
|
|
2458
|
+
expiresAt: null,
|
|
2459
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2460
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
/**
|
|
2464
|
+
* Create a server-side cart via API. Never falls back to localStorage.
|
|
2465
|
+
* @internal
|
|
2466
|
+
*/
|
|
2467
|
+
async createServerCart() {
|
|
2468
|
+
if (this.isVibeCodedMode()) {
|
|
2469
|
+
return this.vibeCodedRequest("POST", "/cart");
|
|
2470
|
+
}
|
|
2471
|
+
if (this.storeId && !this.apiKey) {
|
|
2472
|
+
return this.storefrontRequest("POST", "/cart");
|
|
2473
|
+
}
|
|
2474
|
+
return this.adminRequest("POST", "/api/v1/cart");
|
|
2475
|
+
}
|
|
2476
|
+
/**
|
|
2477
|
+
* Get or create a server-side session cart for guest users.
|
|
2478
|
+
* Lazily creates the cart on first call (e.g., first add-to-cart).
|
|
2479
|
+
* Uses promise dedup lock to prevent race conditions on parallel calls.
|
|
2480
|
+
* @internal
|
|
2481
|
+
*/
|
|
2482
|
+
async getOrCreateSessionCart() {
|
|
2483
|
+
if (this._sessionCartPromise) return this._sessionCartPromise;
|
|
2484
|
+
this._sessionCartPromise = this._getOrCreateSessionCartImpl();
|
|
2485
|
+
try {
|
|
2486
|
+
return await this._sessionCartPromise;
|
|
2487
|
+
} finally {
|
|
2488
|
+
this._sessionCartPromise = null;
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
/** @internal */
|
|
2492
|
+
async _getOrCreateSessionCartImpl() {
|
|
2493
|
+
const migrated = await this.migrateLocalCartToSession();
|
|
2494
|
+
if (migrated) return migrated;
|
|
2495
|
+
if (this.sessionCartId && this.sessionToken) {
|
|
2496
|
+
try {
|
|
2497
|
+
const cart2 = await this.getCart(this.sessionCartId);
|
|
2498
|
+
if (cart2.status === "ACTIVE") {
|
|
2499
|
+
return cart2;
|
|
2500
|
+
}
|
|
2501
|
+
this.clearSessionCart();
|
|
2502
|
+
} catch {
|
|
2503
|
+
this.clearSessionCart();
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
if (this.sessionToken) {
|
|
2507
|
+
try {
|
|
2508
|
+
const cart2 = await this.getCartBySession(this.sessionToken);
|
|
2509
|
+
if (cart2 && cart2.status === "ACTIVE") {
|
|
2510
|
+
this.saveSessionCart({
|
|
2511
|
+
sessionToken: this.sessionToken,
|
|
2512
|
+
cartId: cart2.id,
|
|
2513
|
+
itemCount: cart2.itemCount
|
|
2514
|
+
});
|
|
2515
|
+
return cart2;
|
|
2516
|
+
}
|
|
2517
|
+
} catch {
|
|
2518
|
+
this.clearSessionCart();
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
const cart = await this.createServerCart();
|
|
2522
|
+
if (cart.sessionToken) {
|
|
2523
|
+
this.saveSessionCart({
|
|
2524
|
+
sessionToken: cart.sessionToken,
|
|
2525
|
+
cartId: cart.id,
|
|
2526
|
+
itemCount: cart.itemCount
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
return cart;
|
|
2530
|
+
}
|
|
2531
|
+
/**
|
|
2532
|
+
* Migrate legacy localStorage cart to server session cart.
|
|
2533
|
+
* Called once, on first smart method call if migration is needed.
|
|
2534
|
+
* @internal
|
|
2535
|
+
*/
|
|
2536
|
+
async migrateLocalCartToSession() {
|
|
2537
|
+
if (this._migrationDone) return null;
|
|
2538
|
+
this._migrationDone = true;
|
|
2539
|
+
if (this.sessionToken || this.sessionCartId) return null;
|
|
2540
|
+
const localCart = this.getLocalCart();
|
|
2541
|
+
if (localCart.items.length === 0) return null;
|
|
2542
|
+
try {
|
|
2543
|
+
const cart = await this.createServerCart();
|
|
2544
|
+
for (const item of localCart.items) {
|
|
2545
|
+
try {
|
|
2546
|
+
await this.addToCart(cart.id, {
|
|
2547
|
+
productId: item.productId,
|
|
2548
|
+
variantId: item.variantId,
|
|
2549
|
+
quantity: item.quantity
|
|
2550
|
+
});
|
|
2551
|
+
} catch {
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
if (cart.sessionToken) {
|
|
2555
|
+
this.saveSessionCart({
|
|
2556
|
+
sessionToken: cart.sessionToken,
|
|
2557
|
+
cartId: cart.id
|
|
2558
|
+
});
|
|
2559
|
+
}
|
|
2560
|
+
this.clearLocalCart();
|
|
2561
|
+
return this.getCart(cart.id);
|
|
2562
|
+
} catch {
|
|
2563
|
+
this._migrationDone = false;
|
|
2564
|
+
return null;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
/**
|
|
2568
|
+
* Merge the guest session cart into the customer's cart on the server.
|
|
2569
|
+
* Tries linkCart first (fast, single call), falls back to mergeCarts endpoint.
|
|
2570
|
+
* @internal
|
|
2571
|
+
*/
|
|
2572
|
+
async mergeSessionCartOnLogin() {
|
|
2573
|
+
if (this.sessionCartId) {
|
|
2574
|
+
try {
|
|
2575
|
+
return await this.linkCart(this.sessionCartId);
|
|
2576
|
+
} catch {
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
if (this.isVibeCodedMode()) {
|
|
2580
|
+
return this.vibeCodedRequest("POST", "/cart/merge", {
|
|
2581
|
+
sessionToken: this.sessionToken
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2584
|
+
if (this.storeId && !this.apiKey) {
|
|
2585
|
+
return this.storefrontRequest("POST", "/cart/merge", {
|
|
2586
|
+
sessionToken: this.sessionToken
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2589
|
+
return this.adminRequest("POST", "/api/v1/cart/merge", {
|
|
2590
|
+
sessionToken: this.sessionToken
|
|
2591
|
+
});
|
|
2592
|
+
}
|
|
2593
|
+
/**
|
|
2594
|
+
* Get the cart item count without a full cart fetch.
|
|
2595
|
+
* Returns the cached count from localStorage if available, or 0.
|
|
2596
|
+
* For accurate counts, use smartGetCart() and read cart.itemCount.
|
|
2597
|
+
*/
|
|
2598
|
+
getSmartCartItemCount() {
|
|
2599
|
+
if (this.isCustomerLoggedIn()) {
|
|
2600
|
+
return 0;
|
|
2601
|
+
}
|
|
2602
|
+
if (!this.isLocalStorageAvailable()) return 0;
|
|
2603
|
+
try {
|
|
2604
|
+
const stored = window.localStorage.getItem(this.SESSION_CART_KEY);
|
|
2605
|
+
if (stored) {
|
|
2606
|
+
const ref = JSON.parse(stored);
|
|
2607
|
+
return ref.itemCount ?? 0;
|
|
2608
|
+
}
|
|
2609
|
+
} catch {
|
|
2610
|
+
}
|
|
2611
|
+
return 0;
|
|
2612
|
+
}
|
|
2613
|
+
// -------------------- Customer Cart --------------------
|
|
2397
2614
|
/**
|
|
2398
2615
|
* Get or create a server cart for the logged-in customer
|
|
2399
2616
|
* Caches the cart ID for subsequent calls
|
|
@@ -2446,10 +2663,10 @@ var BrainerceClient = class {
|
|
|
2446
2663
|
throw new BrainerceError("fetchCustomerCart requires vibe-coded or storefront mode", 400);
|
|
2447
2664
|
}
|
|
2448
2665
|
/**
|
|
2449
|
-
* Smart add to cart - automatically uses
|
|
2666
|
+
* Smart add to cart - automatically uses the correct cart based on auth state
|
|
2450
2667
|
*
|
|
2451
|
-
* - **
|
|
2452
|
-
* - **
|
|
2668
|
+
* - **Logged in**: Uses customer's server cart
|
|
2669
|
+
* - **Guest**: Uses server-side session cart (creates one if needed)
|
|
2453
2670
|
*
|
|
2454
2671
|
* @example
|
|
2455
2672
|
* ```typescript
|
|
@@ -2457,25 +2674,35 @@ var BrainerceClient = class {
|
|
|
2457
2674
|
* await client.smartAddToCart({
|
|
2458
2675
|
* productId: 'prod_123',
|
|
2459
2676
|
* quantity: 2,
|
|
2460
|
-
* name: 'Cool Product', // Optional: for localStorage display
|
|
2461
|
-
* price: '29.99',
|
|
2462
2677
|
* });
|
|
2463
2678
|
* ```
|
|
2464
2679
|
*/
|
|
2465
2680
|
async smartAddToCart(item) {
|
|
2466
2681
|
if (this.isCustomerLoggedIn()) {
|
|
2467
2682
|
const cart = await this.getOrCreateCustomerCart();
|
|
2468
|
-
|
|
2683
|
+
const updated = await this.addToCart(cart.id, {
|
|
2469
2684
|
productId: item.productId,
|
|
2470
2685
|
variantId: item.variantId,
|
|
2471
2686
|
quantity: item.quantity
|
|
2472
2687
|
});
|
|
2688
|
+
return updated;
|
|
2473
2689
|
} else {
|
|
2474
|
-
|
|
2690
|
+
const cart = await this.getOrCreateSessionCart();
|
|
2691
|
+
const updated = await this.addToCart(cart.id, {
|
|
2692
|
+
productId: item.productId,
|
|
2693
|
+
variantId: item.variantId,
|
|
2694
|
+
quantity: item.quantity
|
|
2695
|
+
});
|
|
2696
|
+
this.updateSessionCartItemCount(updated.items?.length ?? 0);
|
|
2697
|
+
return updated;
|
|
2475
2698
|
}
|
|
2476
2699
|
}
|
|
2477
2700
|
/**
|
|
2478
|
-
* Smart get cart - returns
|
|
2701
|
+
* Smart get cart - returns the current cart (server-side for both guests and logged-in users)
|
|
2702
|
+
*
|
|
2703
|
+
* - **Logged in**: Returns customer's server cart
|
|
2704
|
+
* - **Guest with session**: Returns server-side session cart
|
|
2705
|
+
* - **Guest without session**: Returns empty cart (no server call, cart created lazily on add)
|
|
2479
2706
|
*
|
|
2480
2707
|
* @example
|
|
2481
2708
|
* ```typescript
|
|
@@ -2487,7 +2714,10 @@ var BrainerceClient = class {
|
|
|
2487
2714
|
if (this.isCustomerLoggedIn()) {
|
|
2488
2715
|
return this.getOrCreateCustomerCart();
|
|
2489
2716
|
} else {
|
|
2490
|
-
|
|
2717
|
+
if (!this.sessionToken) {
|
|
2718
|
+
return this.emptyCart();
|
|
2719
|
+
}
|
|
2720
|
+
return this.getOrCreateSessionCart();
|
|
2491
2721
|
}
|
|
2492
2722
|
}
|
|
2493
2723
|
/**
|
|
@@ -2513,7 +2743,24 @@ var BrainerceClient = class {
|
|
|
2513
2743
|
}
|
|
2514
2744
|
return this.updateCartItem(cart.id, item.id, { quantity });
|
|
2515
2745
|
} else {
|
|
2516
|
-
|
|
2746
|
+
if (!this.sessionToken) {
|
|
2747
|
+
return this.emptyCart();
|
|
2748
|
+
}
|
|
2749
|
+
const cart = await this.getOrCreateSessionCart();
|
|
2750
|
+
const item = cart.items.find(
|
|
2751
|
+
(i) => i.productId === productId && i.variantId === (variantId ?? null)
|
|
2752
|
+
);
|
|
2753
|
+
if (!item) {
|
|
2754
|
+
return cart;
|
|
2755
|
+
}
|
|
2756
|
+
let updated;
|
|
2757
|
+
if (quantity <= 0) {
|
|
2758
|
+
updated = await this.removeCartItem(cart.id, item.id);
|
|
2759
|
+
} else {
|
|
2760
|
+
updated = await this.updateCartItem(cart.id, item.id, { quantity });
|
|
2761
|
+
}
|
|
2762
|
+
this.updateSessionCartItemCount(updated.items?.length ?? 0);
|
|
2763
|
+
return updated;
|
|
2517
2764
|
}
|
|
2518
2765
|
}
|
|
2519
2766
|
/**
|
|
@@ -2529,15 +2776,13 @@ var BrainerceClient = class {
|
|
|
2529
2776
|
return this.smartUpdateCartItem(productId, 0, variantId);
|
|
2530
2777
|
}
|
|
2531
2778
|
/**
|
|
2532
|
-
* Sync
|
|
2779
|
+
* Sync guest cart to customer cart on login
|
|
2533
2780
|
*
|
|
2534
2781
|
* Call this AFTER setCustomerToken() when a customer logs in.
|
|
2535
2782
|
* 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.
|
|
2783
|
+
* 1. If a server-side session cart exists, merge it into the customer cart (atomic, server-side)
|
|
2784
|
+
* 2. If only a legacy localStorage cart exists, sync items one by one (backward compat)
|
|
2785
|
+
* 3. Clear the session/localStorage after merge
|
|
2541
2786
|
*
|
|
2542
2787
|
* @example
|
|
2543
2788
|
* ```typescript
|
|
@@ -2545,7 +2790,7 @@ var BrainerceClient = class {
|
|
|
2545
2790
|
* const auth = await client.login(email, password);
|
|
2546
2791
|
* client.setCustomerToken(auth.token);
|
|
2547
2792
|
*
|
|
2548
|
-
* //
|
|
2793
|
+
* // Merge their guest cart into customer cart
|
|
2549
2794
|
* const cart = await client.syncCartOnLogin();
|
|
2550
2795
|
* console.log('Cart synced, items:', cart.items.length);
|
|
2551
2796
|
* ```
|
|
@@ -2557,6 +2802,14 @@ var BrainerceClient = class {
|
|
|
2557
2802
|
401
|
|
2558
2803
|
);
|
|
2559
2804
|
}
|
|
2805
|
+
if (this.sessionToken) {
|
|
2806
|
+
try {
|
|
2807
|
+
const merged = await this.mergeSessionCartOnLogin();
|
|
2808
|
+
this.clearSessionCart();
|
|
2809
|
+
return merged;
|
|
2810
|
+
} catch {
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2560
2813
|
const localCart = this.getLocalCart();
|
|
2561
2814
|
const serverCart = await this.getOrCreateCustomerCart();
|
|
2562
2815
|
if (localCart.items.length === 0) {
|
|
@@ -2578,15 +2831,15 @@ var BrainerceClient = class {
|
|
|
2578
2831
|
/**
|
|
2579
2832
|
* Clear cart state on logout
|
|
2580
2833
|
*
|
|
2581
|
-
* Call this when a customer logs out to clear the cached cart ID.
|
|
2582
|
-
* The
|
|
2834
|
+
* Call this when a customer logs out to clear the cached customer cart ID.
|
|
2835
|
+
* The session cart (if any) remains available for the next guest session.
|
|
2583
2836
|
*
|
|
2584
2837
|
* @example
|
|
2585
2838
|
* ```typescript
|
|
2586
2839
|
* client.clearCustomerToken();
|
|
2587
2840
|
* client.onLogout();
|
|
2588
2841
|
*
|
|
2589
|
-
* // Now back to guest mode - cart uses
|
|
2842
|
+
* // Now back to guest mode - cart uses server-side session
|
|
2590
2843
|
* await client.smartAddToCart({ productId: 'prod_123', quantity: 1 });
|
|
2591
2844
|
* ```
|
|
2592
2845
|
*/
|
|
@@ -2604,12 +2857,12 @@ var BrainerceClient = class {
|
|
|
2604
2857
|
* // After payment success
|
|
2605
2858
|
* if (paymentStatus === 'succeeded') {
|
|
2606
2859
|
* client.onCheckoutComplete();
|
|
2607
|
-
* client.clearLocalCart(); // Also clear localStorage for guests
|
|
2608
2860
|
* }
|
|
2609
2861
|
* ```
|
|
2610
2862
|
*/
|
|
2611
2863
|
onCheckoutComplete() {
|
|
2612
2864
|
this.customerCartId = null;
|
|
2865
|
+
this.clearSessionCart();
|
|
2613
2866
|
}
|
|
2614
2867
|
// -------------------- Checkout --------------------
|
|
2615
2868
|
/**
|
|
@@ -2636,7 +2889,7 @@ var BrainerceClient = class {
|
|
|
2636
2889
|
async createCheckout(data) {
|
|
2637
2890
|
if (data.cartId === this.VIRTUAL_LOCAL_CART_ID) {
|
|
2638
2891
|
throw new BrainerceError(
|
|
2639
|
-
|
|
2892
|
+
'Cannot create checkout from "__local__" cart. Use startGuestCheckout() or smartAddToCart() to create a server-side cart first.',
|
|
2640
2893
|
400
|
|
2641
2894
|
);
|
|
2642
2895
|
}
|
|
@@ -2705,6 +2958,46 @@ var BrainerceClient = class {
|
|
|
2705
2958
|
}
|
|
2706
2959
|
return this.adminRequest("PATCH", `/api/v1/checkout/${checkoutId}/customer`, data);
|
|
2707
2960
|
}
|
|
2961
|
+
/**
|
|
2962
|
+
* Get available shipping destinations for this store.
|
|
2963
|
+
*
|
|
2964
|
+
* Returns which countries and regions the store ships to,
|
|
2965
|
+
* so you can populate `<select>` dropdowns in your checkout form.
|
|
2966
|
+
*
|
|
2967
|
+
* @example
|
|
2968
|
+
* ```typescript
|
|
2969
|
+
* const dest = await client.getShippingDestinations();
|
|
2970
|
+
*
|
|
2971
|
+
* if (dest.worldwide) {
|
|
2972
|
+
* // Store ships everywhere — use a full country list
|
|
2973
|
+
* } else {
|
|
2974
|
+
* // Only show available countries
|
|
2975
|
+
* dest.countries.forEach(c => console.log(c.code, c.name));
|
|
2976
|
+
* }
|
|
2977
|
+
*
|
|
2978
|
+
* // Check region restrictions for a country
|
|
2979
|
+
* const usRegions = dest.regions['US'];
|
|
2980
|
+
* if (usRegions) {
|
|
2981
|
+
* // Only these US states are available
|
|
2982
|
+
* usRegions.forEach(r => console.log(r.code, r.name));
|
|
2983
|
+
* }
|
|
2984
|
+
* ```
|
|
2985
|
+
*/
|
|
2986
|
+
async getShippingDestinations() {
|
|
2987
|
+
if (this.isVibeCodedMode()) {
|
|
2988
|
+
return this.vibeCodedRequest("GET", "/shipping/destinations");
|
|
2989
|
+
}
|
|
2990
|
+
if (this.storeId && !this.apiKey) {
|
|
2991
|
+
return this.storefrontRequest(
|
|
2992
|
+
"GET",
|
|
2993
|
+
"/checkouts/shipping-destinations"
|
|
2994
|
+
);
|
|
2995
|
+
}
|
|
2996
|
+
return this.adminRequest(
|
|
2997
|
+
"GET",
|
|
2998
|
+
"/api/v1/checkouts/shipping-destinations"
|
|
2999
|
+
);
|
|
3000
|
+
}
|
|
2708
3001
|
/**
|
|
2709
3002
|
* Set shipping address on checkout (includes customer email).
|
|
2710
3003
|
* Returns the checkout and available shipping rates for the address.
|
|
@@ -3172,14 +3465,8 @@ var BrainerceClient = class {
|
|
|
3172
3465
|
}
|
|
3173
3466
|
}
|
|
3174
3467
|
/**
|
|
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
|
-
* ```
|
|
3468
|
+
* @deprecated Use `smartGetCart()` instead. Guest carts are now stored on the server.
|
|
3469
|
+
* Get local cart from localStorage. Returns empty cart if none exists.
|
|
3183
3470
|
*/
|
|
3184
3471
|
getLocalCart() {
|
|
3185
3472
|
if (!this.isLocalStorageAvailable()) {
|
|
@@ -3261,18 +3548,8 @@ var BrainerceClient = class {
|
|
|
3261
3548
|
};
|
|
3262
3549
|
}
|
|
3263
3550
|
/**
|
|
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
|
-
* ```
|
|
3551
|
+
* @deprecated Use `smartAddToCart()` instead. Guest carts are now stored on the server.
|
|
3552
|
+
* Add item to local cart (NO API call). If item already exists, updates quantity.
|
|
3276
3553
|
*/
|
|
3277
3554
|
addToLocalCart(item) {
|
|
3278
3555
|
const cart = this.getLocalCart();
|
|
@@ -3294,14 +3571,8 @@ var BrainerceClient = class {
|
|
|
3294
3571
|
return cart;
|
|
3295
3572
|
}
|
|
3296
3573
|
/**
|
|
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
|
-
* ```
|
|
3574
|
+
* @deprecated Use `smartUpdateCartItem()` instead. Guest carts are now stored on the server.
|
|
3575
|
+
* Update item quantity in local cart. Set quantity to 0 to remove item.
|
|
3305
3576
|
*/
|
|
3306
3577
|
updateLocalCartItem(productId, quantity, variantId) {
|
|
3307
3578
|
const cart = this.getLocalCart();
|
|
@@ -3319,24 +3590,13 @@ var BrainerceClient = class {
|
|
|
3319
3590
|
return cart;
|
|
3320
3591
|
}
|
|
3321
3592
|
/**
|
|
3322
|
-
*
|
|
3323
|
-
*
|
|
3324
|
-
* @example
|
|
3325
|
-
* ```typescript
|
|
3326
|
-
* client.removeFromLocalCart('prod_123');
|
|
3327
|
-
* client.removeFromLocalCart('prod_456', 'variant_789');
|
|
3328
|
-
* ```
|
|
3593
|
+
* @deprecated Use `smartRemoveFromCart()` instead. Guest carts are now stored on the server.
|
|
3329
3594
|
*/
|
|
3330
3595
|
removeFromLocalCart(productId, variantId) {
|
|
3331
3596
|
return this.updateLocalCartItem(productId, 0, variantId);
|
|
3332
3597
|
}
|
|
3333
3598
|
/**
|
|
3334
|
-
*
|
|
3335
|
-
*
|
|
3336
|
-
* @example
|
|
3337
|
-
* ```typescript
|
|
3338
|
-
* client.clearLocalCart();
|
|
3339
|
-
* ```
|
|
3599
|
+
* @deprecated Use `onCheckoutComplete()` instead. Guest carts are now stored on the server.
|
|
3340
3600
|
*/
|
|
3341
3601
|
clearLocalCart() {
|
|
3342
3602
|
const cart = { items: [], updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -3344,16 +3604,8 @@ var BrainerceClient = class {
|
|
|
3344
3604
|
return cart;
|
|
3345
3605
|
}
|
|
3346
3606
|
/**
|
|
3347
|
-
*
|
|
3348
|
-
*
|
|
3349
|
-
* @example
|
|
3350
|
-
* ```typescript
|
|
3351
|
-
* client.setLocalCartCustomer({
|
|
3352
|
-
* email: 'john@example.com',
|
|
3353
|
-
* firstName: 'John',
|
|
3354
|
-
* lastName: 'Doe',
|
|
3355
|
-
* });
|
|
3356
|
-
* ```
|
|
3607
|
+
* @deprecated Customer info is now set via checkout address methods.
|
|
3608
|
+
* Still used by `submitGuestOrder()` for backward compatibility.
|
|
3357
3609
|
*/
|
|
3358
3610
|
setLocalCartCustomer(customer) {
|
|
3359
3611
|
const cart = this.getLocalCart();
|
|
@@ -3362,19 +3614,8 @@ var BrainerceClient = class {
|
|
|
3362
3614
|
return cart;
|
|
3363
3615
|
}
|
|
3364
3616
|
/**
|
|
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
|
-
* ```
|
|
3617
|
+
* @deprecated Address is now set via `updateGuestCheckoutAddress()` after `startGuestCheckout()`.
|
|
3618
|
+
* Still used by `submitGuestOrder()` for backward compatibility.
|
|
3378
3619
|
*/
|
|
3379
3620
|
setLocalCartShippingAddress(address) {
|
|
3380
3621
|
const cart = this.getLocalCart();
|
|
@@ -3383,7 +3624,8 @@ var BrainerceClient = class {
|
|
|
3383
3624
|
return cart;
|
|
3384
3625
|
}
|
|
3385
3626
|
/**
|
|
3386
|
-
*
|
|
3627
|
+
* @deprecated Address is now set via `updateGuestCheckoutAddress()` after `startGuestCheckout()`.
|
|
3628
|
+
* Still used by `submitGuestOrder()` for backward compatibility.
|
|
3387
3629
|
*/
|
|
3388
3630
|
setLocalCartBillingAddress(address) {
|
|
3389
3631
|
const cart = this.getLocalCart();
|
|
@@ -3392,7 +3634,7 @@ var BrainerceClient = class {
|
|
|
3392
3634
|
return cart;
|
|
3393
3635
|
}
|
|
3394
3636
|
/**
|
|
3395
|
-
*
|
|
3637
|
+
* @deprecated Coupons are now applied via `applyCoupon()` on the server cart.
|
|
3396
3638
|
*/
|
|
3397
3639
|
setLocalCartCoupon(couponCode) {
|
|
3398
3640
|
const cart = this.getLocalCart();
|
|
@@ -3401,7 +3643,7 @@ var BrainerceClient = class {
|
|
|
3401
3643
|
return cart;
|
|
3402
3644
|
}
|
|
3403
3645
|
/**
|
|
3404
|
-
*
|
|
3646
|
+
* @deprecated Use `getSmartCartItemCount()` instead.
|
|
3405
3647
|
*/
|
|
3406
3648
|
getLocalCartItemCount() {
|
|
3407
3649
|
const cart = this.getLocalCart();
|
|
@@ -3431,6 +3673,46 @@ var BrainerceClient = class {
|
|
|
3431
3673
|
* ```
|
|
3432
3674
|
*/
|
|
3433
3675
|
async submitGuestOrder(options) {
|
|
3676
|
+
if (this.sessionToken) {
|
|
3677
|
+
const serverCart = await this.getOrCreateSessionCart();
|
|
3678
|
+
if (!serverCart.items || serverCart.items.length === 0) {
|
|
3679
|
+
throw new BrainerceError("Cart is empty", 400);
|
|
3680
|
+
}
|
|
3681
|
+
const localCart = this.getLocalCart();
|
|
3682
|
+
if (!localCart.customer?.email) {
|
|
3683
|
+
throw new BrainerceError(
|
|
3684
|
+
"Customer email is required. Call setLocalCartCustomer() first.",
|
|
3685
|
+
400
|
|
3686
|
+
);
|
|
3687
|
+
}
|
|
3688
|
+
if (!localCart.shippingAddress) {
|
|
3689
|
+
throw new BrainerceError(
|
|
3690
|
+
"Shipping address is required. Call setLocalCartShippingAddress() first.",
|
|
3691
|
+
400
|
|
3692
|
+
);
|
|
3693
|
+
}
|
|
3694
|
+
const trackingResult = await this.startGuestCheckout();
|
|
3695
|
+
if (!trackingResult.tracked) {
|
|
3696
|
+
throw new BrainerceError("Failed to create checkout from session cart", 500);
|
|
3697
|
+
}
|
|
3698
|
+
await this.updateGuestCheckoutAddress(trackingResult.checkoutId, {
|
|
3699
|
+
shippingAddress: {
|
|
3700
|
+
...localCart.shippingAddress,
|
|
3701
|
+
email: localCart.customer.email
|
|
3702
|
+
},
|
|
3703
|
+
billingAddress: localCart.billingAddress
|
|
3704
|
+
});
|
|
3705
|
+
const orderResult = await this.completeGuestCheckout(trackingResult.checkoutId, {
|
|
3706
|
+
clearCartOnSuccess: options?.clearCartOnSuccess
|
|
3707
|
+
});
|
|
3708
|
+
return {
|
|
3709
|
+
orderId: orderResult.orderId,
|
|
3710
|
+
orderNumber: orderResult.orderNumber || orderResult.orderId,
|
|
3711
|
+
status: orderResult.status || "pending",
|
|
3712
|
+
total: orderResult.total ?? 0,
|
|
3713
|
+
message: orderResult.message || "Order created successfully"
|
|
3714
|
+
};
|
|
3715
|
+
}
|
|
3434
3716
|
const cart = this.getLocalCart();
|
|
3435
3717
|
if (cart.items.length === 0) {
|
|
3436
3718
|
throw new BrainerceError("Cart is empty", 400);
|
|
@@ -3448,7 +3730,6 @@ var BrainerceClient = class {
|
|
|
3448
3730
|
shippingAddress: {
|
|
3449
3731
|
...cart.shippingAddress,
|
|
3450
3732
|
email: cart.customer.email
|
|
3451
|
-
// Already validated above
|
|
3452
3733
|
},
|
|
3453
3734
|
billingAddress: cart.billingAddress
|
|
3454
3735
|
});
|
|
@@ -3536,6 +3817,34 @@ var BrainerceClient = class {
|
|
|
3536
3817
|
* ```
|
|
3537
3818
|
*/
|
|
3538
3819
|
async startGuestCheckout(options) {
|
|
3820
|
+
if (this.sessionToken) {
|
|
3821
|
+
const serverCart = await this.getOrCreateSessionCart();
|
|
3822
|
+
if (!serverCart.items || serverCart.items.length === 0) {
|
|
3823
|
+
throw new BrainerceError("Cart is empty", 400);
|
|
3824
|
+
}
|
|
3825
|
+
let selectedItemIds;
|
|
3826
|
+
if (options?.selectedIndices?.length) {
|
|
3827
|
+
selectedItemIds = options.selectedIndices.map((i) => serverCart.items[i]?.id).filter((id) => !!id);
|
|
3828
|
+
if (selectedItemIds.length === 0) {
|
|
3829
|
+
throw new BrainerceError("No items selected for checkout", 400);
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
const checkout = await this.createCheckout({
|
|
3833
|
+
cartId: serverCart.id,
|
|
3834
|
+
selectedItemIds
|
|
3835
|
+
});
|
|
3836
|
+
this.saveActiveCheckout({
|
|
3837
|
+
checkoutId: checkout.id,
|
|
3838
|
+
cartId: serverCart.id,
|
|
3839
|
+
selectedIndices: options?.selectedIndices
|
|
3840
|
+
});
|
|
3841
|
+
return {
|
|
3842
|
+
tracked: true,
|
|
3843
|
+
checkoutId: checkout.id,
|
|
3844
|
+
cartId: serverCart.id,
|
|
3845
|
+
message: "Checkout created from session cart"
|
|
3846
|
+
};
|
|
3847
|
+
}
|
|
3539
3848
|
const cart = this.getLocalCart();
|
|
3540
3849
|
if (cart.items.length === 0) {
|
|
3541
3850
|
throw new BrainerceError("Cart is empty", 400);
|
|
@@ -3599,16 +3908,8 @@ var BrainerceClient = class {
|
|
|
3599
3908
|
}
|
|
3600
3909
|
}
|
|
3601
3910
|
/**
|
|
3911
|
+
* @deprecated Partial checkout is now handled server-side via `selectedItemIds`.
|
|
3602
3912
|
* 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
3913
|
*/
|
|
3613
3914
|
removeLocalCartItemsByIndex(indices) {
|
|
3614
3915
|
const cart = this.getLocalCart();
|
|
@@ -3685,6 +3986,7 @@ var BrainerceClient = class {
|
|
|
3685
3986
|
this.removeLocalCartItemsByIndex(indices);
|
|
3686
3987
|
} else {
|
|
3687
3988
|
this.clearLocalCart();
|
|
3989
|
+
this.clearSessionCart();
|
|
3688
3990
|
}
|
|
3689
3991
|
this.customerCartId = null;
|
|
3690
3992
|
}
|
|
@@ -3782,6 +4084,7 @@ var BrainerceClient = class {
|
|
|
3782
4084
|
}
|
|
3783
4085
|
if (isLoggedIn) {
|
|
3784
4086
|
this.clearLocalCart();
|
|
4087
|
+
this.clearSessionCart();
|
|
3785
4088
|
return {
|
|
3786
4089
|
cleared: true,
|
|
3787
4090
|
mode: "full",
|
|
@@ -3798,6 +4101,7 @@ var BrainerceClient = class {
|
|
|
3798
4101
|
};
|
|
3799
4102
|
}
|
|
3800
4103
|
this.clearLocalCart();
|
|
4104
|
+
this.clearSessionCart();
|
|
3801
4105
|
return {
|
|
3802
4106
|
cleared: true,
|
|
3803
4107
|
mode: "full",
|