omni-sync-sdk 0.16.2 → 0.17.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/README.md CHANGED
@@ -26,15 +26,26 @@ The SDK exports these utility functions for common UI tasks:
26
26
  | `getPriceDisplay(amount, currency?, locale?)` | Alias for `formatPrice` | Same as above |
27
27
  | `getDescriptionContent(product)` | Get product description (HTML or text) | `getDescriptionContent(product)` |
28
28
  | `isHtmlDescription(product)` | Check if description is HTML | `isHtmlDescription(product)` → `true/false` |
29
- | `getStockStatus(variant)` | Get human-readable stock status | `getStockStatus(variant)` → `"In Stock"` |
29
+ | `getStockStatus(inventory)` | Get human-readable stock status | `getStockStatus(inventory)` → `"In Stock"` |
30
+ | `getProductPrice(product)` | Get effective price (handles sales) | `getProductPrice(product)` → `29.99` |
31
+ | `getProductPriceInfo(product)` | Get price + sale info + discount % | `{ price, isOnSale, discountPercent }` |
32
+ | `getVariantPrice(variant, basePrice)` | Get variant price with fallback | `getVariantPrice(variant, '29.99')` → `34.99` |
33
+ | `getCartTotals(cart, shippingPrice?)` | Calculate cart subtotal/discount/total | `{ subtotal, discount, shipping, total }` |
34
+ | `getCartItemName(item)` | Get name from nested cart item | `getCartItemName(item)` → `"Blue T-Shirt"` |
35
+ | `getCartItemImage(item)` | Get image URL from cart item | `getCartItemImage(item)` → `"https://..."` |
36
+ | `getVariantOptions(variant)` | Get variant attributes as array | `[{ name: "Color", value: "Red" }]` |
30
37
  | `isCouponApplicableToProduct(coupon, product)` | Check if coupon applies | `isCouponApplicableToProduct(coupon, product)` |
31
38
 
32
39
  ```typescript
33
40
  import {
34
41
  formatPrice,
35
- getPriceDisplay, // Alias for formatPrice
36
42
  getDescriptionContent,
37
43
  getStockStatus,
44
+ getProductPrice,
45
+ getProductPriceInfo,
46
+ getCartTotals,
47
+ getCartItemName,
48
+ getCartItemImage,
38
49
  } from 'omni-sync-sdk';
39
50
 
40
51
  // Format price for display
@@ -44,9 +55,26 @@ const priceText = formatPrice(product.basePrice, 'USD'); // "$99.99"
44
55
  const description = getDescriptionContent(product);
45
56
 
46
57
  // Get stock status text
47
- const stockText = getStockStatus(product.variants[0]); // "In Stock", "Low Stock", "Out of Stock"
58
+ const stockText = getStockStatus(product.inventory); // "In Stock", "Low Stock", "Out of Stock"
59
+
60
+ // Get effective price (handles sale prices automatically)
61
+ const price = getProductPrice(product); // Returns number: 29.99
62
+
63
+ // Get full price info including sale status
64
+ const priceInfo = getProductPriceInfo(product);
65
+ // { price: 19.99, originalPrice: 29.99, isOnSale: true, discountPercent: 33 }
66
+
67
+ // Calculate cart totals
68
+ const totals = getCartTotals(cart, shippingRate?.price);
69
+ // { subtotal: 59.98, discount: 10, shipping: 5.99, total: 55.97 }
70
+
71
+ // Access cart item details (handles nested structure)
72
+ const itemName = getCartItemName(cartItem); // "Blue T-Shirt - Large"
73
+ const itemImage = getCartItemImage(cartItem); // "https://..."
48
74
  ```
49
75
 
76
+ > **⚠️ DO NOT CREATE YOUR OWN UTILITY FILES!** All helper functions above are exported from `omni-sync-sdk`. Never create `utils/format.ts`, `lib/helpers.ts`, or similar files - use the SDK exports directly.
77
+
50
78
  ---
51
79
 
52
80
  ## ⚠️ CRITICAL: Payment Integration Required!
@@ -440,9 +468,9 @@ const response: PaginatedResponse<Product> = await omni.getProducts({
440
468
  sortOrder: 'desc', // Optional: 'asc' | 'desc'
441
469
  });
442
470
 
443
- console.log(response.items); // Product[]
444
- console.log(response.total); // Total number of products
445
- console.log(response.totalPages); // Total pages
471
+ console.log(response.data); // Product[]
472
+ console.log(response.meta.total); // Total number of products
473
+ console.log(response.meta.totalPages); // Total pages
446
474
  ```
447
475
 
448
476
  #### Get Single Product
@@ -2306,8 +2334,8 @@ export default function ProductsPage() {
2306
2334
  <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page === 1}>
2307
2335
  Previous
2308
2336
  </button>
2309
- <span>Page {data.page} of {data.totalPages}</span>
2310
- <button onClick={() => setPage(p => p + 1)} disabled={page >= data.totalPages}>
2337
+ <span>Page {data.meta.page} of {data.meta.totalPages}</span>
2338
+ <button onClick={() => setPage(p => p + 1)} disabled={page >= data.meta.totalPages}>
2311
2339
  Next
2312
2340
  </button>
2313
2341
  </div>
@@ -3205,7 +3233,7 @@ export default function AccountPage() {
3205
3233
  omni.getMyOrders({ limit: 10 }),
3206
3234
  ]);
3207
3235
  setProfile(p);
3208
- setOrders(o.items);
3236
+ setOrders(o.data);
3209
3237
  } finally {
3210
3238
  setLoading(false);
3211
3239
  }
package/dist/index.d.mts CHANGED
@@ -148,6 +148,8 @@ interface ProductImage {
148
148
  url: string;
149
149
  position?: number;
150
150
  isMain?: boolean;
151
+ /** Alt text for accessibility */
152
+ alt?: string;
151
153
  }
152
154
  interface ProductVariant {
153
155
  id: string;
@@ -160,6 +162,14 @@ interface ProductVariant {
160
162
  /** Variant attributes (e.g., { "Color": "Red", "Size": "M" }) */
161
163
  attributes?: Record<string, unknown>;
162
164
  inventory?: InventoryInfo | null;
165
+ /** Variant image URL or image object */
166
+ image?: string | {
167
+ url: string;
168
+ } | null;
169
+ /** Display position/order for sorting variants */
170
+ position?: number;
171
+ /** Variant status */
172
+ status?: 'active' | 'draft' | 'archived';
163
173
  }
164
174
  /**
165
175
  * Inventory tracking mode determines how stock is managed:
@@ -328,6 +338,155 @@ interface FormatPriceOptions {
328
338
  * ```
329
339
  */
330
340
  declare function formatPrice(priceString: string | undefined | null, options?: FormatPriceOptions): string | number;
341
+ /**
342
+ * Get the effective price of a product (sale price if on sale, otherwise base price).
343
+ * Returns the price as a NUMBER, not string.
344
+ *
345
+ * @param product - Product or object with basePrice and optional salePrice
346
+ * @returns The effective price as a number
347
+ *
348
+ * @example
349
+ * ```typescript
350
+ * const product = await omni.getProduct('prod_123');
351
+ * const price = getProductPrice(product); // Returns number, e.g., 29.99
352
+ *
353
+ * // Check if on sale
354
+ * const { price, isOnSale, originalPrice } = getProductPriceInfo(product);
355
+ * ```
356
+ */
357
+ declare function getProductPrice(product: Pick<Product, 'basePrice' | 'salePrice'> | null | undefined): number;
358
+ /**
359
+ * Get detailed price information for a product including sale status.
360
+ *
361
+ * @param product - Product or object with basePrice and optional salePrice
362
+ * @returns Object with price, originalPrice, isOnSale, and discount info
363
+ *
364
+ * @example
365
+ * ```typescript
366
+ * const info = getProductPriceInfo(product);
367
+ * // info = {
368
+ * // price: 19.99, // Current/effective price
369
+ * // originalPrice: 29.99, // Original price (same as price if not on sale)
370
+ * // isOnSale: true,
371
+ * // discountAmount: 10, // How much is saved
372
+ * // discountPercent: 33 // Percentage off (rounded)
373
+ * // }
374
+ *
375
+ * // In React component:
376
+ * if (info.isOnSale) {
377
+ * return (
378
+ * <div>
379
+ * <span className="text-red-600">${info.price}</span>
380
+ * <span className="line-through">${info.originalPrice}</span>
381
+ * <span className="badge">-{info.discountPercent}%</span>
382
+ * </div>
383
+ * );
384
+ * }
385
+ * ```
386
+ */
387
+ declare function getProductPriceInfo(product: Pick<Product, 'basePrice' | 'salePrice'> | null | undefined): {
388
+ price: number;
389
+ originalPrice: number;
390
+ isOnSale: boolean;
391
+ discountAmount: number;
392
+ discountPercent: number;
393
+ };
394
+ /**
395
+ * Get the price of a variant, falling back to product base price if not set.
396
+ * Returns the price as a NUMBER.
397
+ *
398
+ * @param variant - The product variant
399
+ * @param productBasePrice - The product's base price as fallback
400
+ * @returns The variant price as a number
401
+ *
402
+ * @example
403
+ * ```typescript
404
+ * product.variants?.forEach(variant => {
405
+ * const price = getVariantPrice(variant, product.basePrice);
406
+ * console.log(`${variant.name}: $${price}`);
407
+ * });
408
+ * ```
409
+ */
410
+ declare function getVariantPrice(variant: Pick<ProductVariant, 'price' | 'salePrice'> | null | undefined, productBasePrice: string): number;
411
+ /**
412
+ * Calculate the total for a cart.
413
+ * Cart does NOT have a 'total' field - use this helper!
414
+ *
415
+ * @param cart - The cart object
416
+ * @param shippingPrice - Optional shipping price (string or number)
417
+ * @returns Object with subtotal, discount, shipping, and total
418
+ *
419
+ * @example
420
+ * ```typescript
421
+ * const cart = await omni.getCart(cartId);
422
+ * const totals = getCartTotals(cart);
423
+ * // totals = { subtotal: 59.98, discount: 10, shipping: 0, total: 49.98 }
424
+ *
425
+ * // With shipping
426
+ * const totals = getCartTotals(cart, selectedRate?.price);
427
+ * // totals = { subtotal: 59.98, discount: 0, shipping: 5.99, total: 65.97 }
428
+ * ```
429
+ */
430
+ declare function getCartTotals(cart: Pick<Cart, 'subtotal' | 'discountAmount'> | null | undefined, shippingPrice?: string | number | null): {
431
+ subtotal: number;
432
+ discount: number;
433
+ shipping: number;
434
+ total: number;
435
+ };
436
+ /**
437
+ * Get the display name for a cart item.
438
+ * Handles the nested product/variant structure - uses variant name if available,
439
+ * otherwise falls back to product name.
440
+ *
441
+ * @param item - The cart item
442
+ * @returns The display name for the item
443
+ *
444
+ * @example
445
+ * ```typescript
446
+ * cart.items.forEach(item => {
447
+ * const name = getCartItemName(item);
448
+ * console.log(`${name} x ${item.quantity}`);
449
+ * });
450
+ * ```
451
+ */
452
+ declare function getCartItemName(item: CartItem): string;
453
+ /**
454
+ * Get the image URL for a cart item.
455
+ * Checks variant image first, then falls back to the first product image.
456
+ *
457
+ * @param item - The cart item
458
+ * @returns The image URL or undefined if no image exists
459
+ *
460
+ * @example
461
+ * ```typescript
462
+ * const imageUrl = getCartItemImage(item);
463
+ * if (imageUrl) {
464
+ * return <img src={imageUrl} alt={getCartItemName(item)} />;
465
+ * }
466
+ * ```
467
+ */
468
+ declare function getCartItemImage(item: CartItem): string | undefined;
469
+ /**
470
+ * Convert variant attributes to an array of name/value pairs.
471
+ * Transforms { "Color": "Red", "Size": "M" } to [{ name: "Color", value: "Red" }, ...]
472
+ *
473
+ * @param variant - The product variant (or object with attributes)
474
+ * @returns Array of { name, value } objects
475
+ *
476
+ * @example
477
+ * ```typescript
478
+ * const options = getVariantOptions(selectedVariant);
479
+ * // options = [{ name: "Color", value: "Red" }, { name: "Size", value: "M" }]
480
+ *
481
+ * options.forEach(opt => {
482
+ * console.log(`${opt.name}: ${opt.value}`);
483
+ * });
484
+ * ```
485
+ */
486
+ declare function getVariantOptions(variant: Pick<ProductVariant, 'attributes'> | null | undefined): Array<{
487
+ name: string;
488
+ value: string;
489
+ }>;
331
490
  interface ProductQueryParams {
332
491
  page?: number;
333
492
  limit?: number;
@@ -419,10 +578,22 @@ interface UpdateProductDto {
419
578
  interface Order {
420
579
  id: string;
421
580
  externalId?: string;
581
+ /** Human-readable order number (e.g., "#1001") */
582
+ orderNumber?: string;
422
583
  status: OrderStatus;
423
- totalAmount: number;
424
- customer: OrderCustomer;
584
+ /** Total amount as string (e.g., "99.99"). Use parseFloat() for calculations. */
585
+ totalAmount: string;
586
+ /** Currency code (e.g., "USD", "ILS") */
587
+ currency?: string;
588
+ /** Customer info (may be null for guest orders) */
589
+ customer?: OrderCustomer | null;
425
590
  items: OrderItem[];
591
+ /** Number of items in the order */
592
+ itemCount?: number;
593
+ /** Shipping address for the order */
594
+ shippingAddress?: OrderAddress;
595
+ /** Billing address for the order */
596
+ billingAddress?: OrderAddress;
426
597
  platform?: string;
427
598
  createdAt: string;
428
599
  }
@@ -434,20 +605,32 @@ interface OrderCustomer {
434
605
  address?: OrderAddress;
435
606
  }
436
607
  interface OrderAddress {
608
+ firstName?: string;
609
+ lastName?: string;
610
+ company?: string;
437
611
  line1: string;
438
612
  line2?: string;
439
613
  city: string;
614
+ /** State/Province (alias: use `state` or `region`) */
440
615
  state?: string;
616
+ /** Region/Province (alias: use `state` or `region`) */
617
+ region?: string;
441
618
  postalCode: string;
442
619
  country: string;
620
+ phone?: string;
443
621
  }
444
622
  interface OrderItem {
445
623
  productId: string;
624
+ variantId?: string;
446
625
  sku?: string;
447
626
  name?: string;
448
627
  quantity: number;
628
+ /** Price per unit as number */
449
629
  price: number;
450
- variantId?: string;
630
+ /** Unit price as string (for consistency with Cart/Checkout) */
631
+ unitPrice?: string;
632
+ /** Product image URL */
633
+ image?: string;
451
634
  }
452
635
  interface OrderQueryParams {
453
636
  page?: number;
@@ -1995,6 +2178,11 @@ declare class OmniSyncClient {
1995
2178
  private readonly timeout;
1996
2179
  private customerToken;
1997
2180
  private customerCartId;
2181
+ /**
2182
+ * Virtual cart ID used for localStorage carts (guest users not logged in).
2183
+ * When a cart has this ID, operations use localStorage instead of server API.
2184
+ */
2185
+ private readonly VIRTUAL_LOCAL_CART_ID;
1998
2186
  private readonly onAuthError?;
1999
2187
  constructor(options: OmniSyncClientOptions);
2000
2188
  /**
@@ -3454,6 +3642,16 @@ declare class OmniSyncClient {
3454
3642
  * Save local cart to localStorage
3455
3643
  */
3456
3644
  private saveLocalCart;
3645
+ /**
3646
+ * Calculate subtotal for a local cart
3647
+ */
3648
+ private calculateLocalCartSubtotal;
3649
+ /**
3650
+ * Convert LocalCart to Cart format for consistent API.
3651
+ * This allows cart methods to return the same Cart type regardless
3652
+ * of whether data is stored locally or on the server.
3653
+ */
3654
+ private localCartToCart;
3457
3655
  /**
3458
3656
  * Add item to local cart (NO API call)
3459
3657
  * If item already exists, updates quantity
@@ -3923,4 +4121,4 @@ declare function isWebhookEventType(event: WebhookEvent, type: WebhookEventType)
3923
4121
  */
3924
4122
  declare function createWebhookHandler(handlers: Partial<Record<WebhookEventType, (event: WebhookEvent) => Promise<void>>>): (payload: unknown) => Promise<void>;
3925
4123
 
3926
- export { type AddToCartDto, type AppliedDiscount, type ApplyCouponDto, type BulkInventoryResponse, type BulkSaveVariantsDto, type BulkSaveVariantsResponse, type BulkVariantInput, type Cart, type CartItem, type CartStatus, type CategorySuggestion, type Checkout, type CheckoutAddress, type CheckoutLineItem, type CheckoutPrefillData, type CheckoutStatus, type CompleteCheckoutResponse, type CompleteDraftDto, type ConnectorPlatform, type Coupon, type CouponCreateResponse, type CouponQueryParams, type CouponStatus, type CouponType, type CouponValidationWarning, type CreateAddressDto, type CreateCheckoutDto, type CreateCouponDto, type CreateCustomApiDto, type CreateCustomerDto, type CreateGuestOrderDto, type CreateOrderDto, type CreateProductDto, type CreateRefundDto, type CreateVariantDto, type CustomApiAuthType, type CustomApiConnectionStatus, type CustomApiCredentials, type CustomApiIntegration, type CustomApiSyncConfig, type CustomApiSyncDirection, type CustomApiTestResult, type Customer, type CustomerAddress, type CustomerAuthResponse, type CustomerOAuthProvider, type CustomerProfile, type CustomerQueryParams, type DraftLineItem, type EditInventoryDto, type EmailVerificationResponse, type FormatPriceOptions, type FulfillOrderDto, type GuestCheckoutStartResponse, type GuestOrderResponse, type InsufficientStockError, type InventoryInfo, type InventorySyncStatus, type LocalCart, type LocalCartItem, type MergeCartsDto, type OAuthAuthorizeResponse, type OAuthCallbackResponse, type OAuthConnection, type OAuthConnectionsResponse, type OAuthProvidersResponse, type OmniSyncApiError, OmniSyncClient, type OmniSyncClientOptions, OmniSyncError, type Order, type OrderAddress, type OrderCustomer, type OrderItem, type OrderQueryParams, type OrderStatus, type PaginatedResponse, type PaymentConfig, type PaymentIntent, type PaymentProviderConfig, type PaymentProvidersConfig, type PaymentStatus, type PlatformCouponCapabilities, type Product, type ProductAttributeInput, type ProductImage, type ProductQueryParams, type ProductSuggestion, type ProductVariant, type PublishProductResponse, type ReconcileInventoryResponse, type Refund, type RefundLineItem, type RefundLineItemResponse, type RefundType, type RegisterCustomerDto, type SearchSuggestions, type SelectShippingMethodDto, type SendInvoiceDto, type SetBillingAddressDto, type SetCheckoutCustomerDto, type SetShippingAddressDto, type SetShippingAddressResponse, type ShippingLine, type ShippingRate, type StockAvailabilityRequest, type StockAvailabilityResponse, type StockAvailabilityResult, type StoreInfo, type SyncJob, type UpdateAddressDto, type UpdateCartItemDto, type UpdateCouponDto, type UpdateCustomApiDto, type UpdateCustomerDto, type UpdateDraftDto, type UpdateInventoryDto, type UpdateOrderDto, type UpdateOrderShippingDto, type UpdateProductDto, type UpdateVariantDto, type UpdateVariantInventoryDto, type VariantInventoryResponse, type VariantPlatformOverlay, type VariantStatus, type WaitForOrderOptions, type WaitForOrderResult, type WebhookEvent, type WebhookEventType, createWebhookHandler, formatPrice, getDescriptionContent, formatPrice as getPriceDisplay, getStockStatus, isCouponApplicableToProduct, isHtmlDescription, isWebhookEventType, parseWebhookEvent, verifyWebhook };
4124
+ export { type AddToCartDto, type AppliedDiscount, type ApplyCouponDto, type BulkInventoryResponse, type BulkSaveVariantsDto, type BulkSaveVariantsResponse, type BulkVariantInput, type Cart, type CartItem, type CartStatus, type CategorySuggestion, type Checkout, type CheckoutAddress, type CheckoutLineItem, type CheckoutPrefillData, type CheckoutStatus, type CompleteCheckoutResponse, type CompleteDraftDto, type ConnectorPlatform, type Coupon, type CouponCreateResponse, type CouponQueryParams, type CouponStatus, type CouponType, type CouponValidationWarning, type CreateAddressDto, type CreateCheckoutDto, type CreateCouponDto, type CreateCustomApiDto, type CreateCustomerDto, type CreateGuestOrderDto, type CreateOrderDto, type CreateProductDto, type CreateRefundDto, type CreateVariantDto, type CustomApiAuthType, type CustomApiConnectionStatus, type CustomApiCredentials, type CustomApiIntegration, type CustomApiSyncConfig, type CustomApiSyncDirection, type CustomApiTestResult, type Customer, type CustomerAddress, type CustomerAuthResponse, type CustomerOAuthProvider, type CustomerProfile, type CustomerQueryParams, type DraftLineItem, type EditInventoryDto, type EmailVerificationResponse, type ExtendReservationResponse, type FormatPriceOptions, type FulfillOrderDto, type GuestCheckoutStartResponse, type GuestOrderResponse, type InsufficientStockError, type InventoryInfo, type InventoryReservationStrategy, type InventorySyncStatus, type InventoryTrackingMode, type LocalCart, type LocalCartItem, type MergeCartsDto, type OAuthAuthorizeResponse, type OAuthCallbackResponse, type OAuthConnection, type OAuthConnectionsResponse, type OAuthProvidersResponse, type OmniSyncApiError, OmniSyncClient, type OmniSyncClientOptions, OmniSyncError, type Order, type OrderAddress, type OrderCustomer, type OrderItem, type OrderQueryParams, type OrderStatus, type PaginatedResponse, type PaymentConfig, type PaymentIntent, type PaymentProviderConfig, type PaymentProvidersConfig, type PaymentStatus, type PlatformCouponCapabilities, type Product, type ProductAttributeInput, type ProductAvailability, type ProductImage, type ProductMetafield, type ProductQueryParams, type ProductSuggestion, type ProductVariant, type PublishProductResponse, type ReconcileInventoryResponse, type Refund, type RefundLineItem, type RefundLineItemResponse, type RefundType, type RegisterCustomerDto, type ReservationInfo, type SearchSuggestions, type SelectShippingMethodDto, type SendInvoiceDto, type SetBillingAddressDto, type SetCheckoutCustomerDto, type SetShippingAddressDto, type SetShippingAddressResponse, type ShippingLine, type ShippingRate, type StockAvailabilityRequest, type StockAvailabilityResponse, type StockAvailabilityResult, type StoreInfo, type SyncJob, type TaxBreakdown, type TaxBreakdownItem, type UpdateAddressDto, type UpdateCartItemDto, type UpdateCouponDto, type UpdateCustomApiDto, type UpdateCustomerDto, type UpdateDraftDto, type UpdateInventoryDto, type UpdateOrderDto, type UpdateOrderShippingDto, type UpdateProductDto, type UpdateVariantDto, type UpdateVariantInventoryDto, type VariantInventoryResponse, type VariantPlatformOverlay, type VariantStatus, type WaitForOrderOptions, type WaitForOrderResult, type WebhookEvent, type WebhookEventType, createWebhookHandler, formatPrice, getCartItemImage, getCartItemName, getCartTotals, getDescriptionContent, formatPrice as getPriceDisplay, getProductPrice, getProductPriceInfo, getStockStatus, getVariantOptions, getVariantPrice, isCouponApplicableToProduct, isHtmlDescription, isWebhookEventType, parseWebhookEvent, verifyWebhook };
package/dist/index.d.ts CHANGED
@@ -148,6 +148,8 @@ interface ProductImage {
148
148
  url: string;
149
149
  position?: number;
150
150
  isMain?: boolean;
151
+ /** Alt text for accessibility */
152
+ alt?: string;
151
153
  }
152
154
  interface ProductVariant {
153
155
  id: string;
@@ -160,6 +162,14 @@ interface ProductVariant {
160
162
  /** Variant attributes (e.g., { "Color": "Red", "Size": "M" }) */
161
163
  attributes?: Record<string, unknown>;
162
164
  inventory?: InventoryInfo | null;
165
+ /** Variant image URL or image object */
166
+ image?: string | {
167
+ url: string;
168
+ } | null;
169
+ /** Display position/order for sorting variants */
170
+ position?: number;
171
+ /** Variant status */
172
+ status?: 'active' | 'draft' | 'archived';
163
173
  }
164
174
  /**
165
175
  * Inventory tracking mode determines how stock is managed:
@@ -328,6 +338,155 @@ interface FormatPriceOptions {
328
338
  * ```
329
339
  */
330
340
  declare function formatPrice(priceString: string | undefined | null, options?: FormatPriceOptions): string | number;
341
+ /**
342
+ * Get the effective price of a product (sale price if on sale, otherwise base price).
343
+ * Returns the price as a NUMBER, not string.
344
+ *
345
+ * @param product - Product or object with basePrice and optional salePrice
346
+ * @returns The effective price as a number
347
+ *
348
+ * @example
349
+ * ```typescript
350
+ * const product = await omni.getProduct('prod_123');
351
+ * const price = getProductPrice(product); // Returns number, e.g., 29.99
352
+ *
353
+ * // Check if on sale
354
+ * const { price, isOnSale, originalPrice } = getProductPriceInfo(product);
355
+ * ```
356
+ */
357
+ declare function getProductPrice(product: Pick<Product, 'basePrice' | 'salePrice'> | null | undefined): number;
358
+ /**
359
+ * Get detailed price information for a product including sale status.
360
+ *
361
+ * @param product - Product or object with basePrice and optional salePrice
362
+ * @returns Object with price, originalPrice, isOnSale, and discount info
363
+ *
364
+ * @example
365
+ * ```typescript
366
+ * const info = getProductPriceInfo(product);
367
+ * // info = {
368
+ * // price: 19.99, // Current/effective price
369
+ * // originalPrice: 29.99, // Original price (same as price if not on sale)
370
+ * // isOnSale: true,
371
+ * // discountAmount: 10, // How much is saved
372
+ * // discountPercent: 33 // Percentage off (rounded)
373
+ * // }
374
+ *
375
+ * // In React component:
376
+ * if (info.isOnSale) {
377
+ * return (
378
+ * <div>
379
+ * <span className="text-red-600">${info.price}</span>
380
+ * <span className="line-through">${info.originalPrice}</span>
381
+ * <span className="badge">-{info.discountPercent}%</span>
382
+ * </div>
383
+ * );
384
+ * }
385
+ * ```
386
+ */
387
+ declare function getProductPriceInfo(product: Pick<Product, 'basePrice' | 'salePrice'> | null | undefined): {
388
+ price: number;
389
+ originalPrice: number;
390
+ isOnSale: boolean;
391
+ discountAmount: number;
392
+ discountPercent: number;
393
+ };
394
+ /**
395
+ * Get the price of a variant, falling back to product base price if not set.
396
+ * Returns the price as a NUMBER.
397
+ *
398
+ * @param variant - The product variant
399
+ * @param productBasePrice - The product's base price as fallback
400
+ * @returns The variant price as a number
401
+ *
402
+ * @example
403
+ * ```typescript
404
+ * product.variants?.forEach(variant => {
405
+ * const price = getVariantPrice(variant, product.basePrice);
406
+ * console.log(`${variant.name}: $${price}`);
407
+ * });
408
+ * ```
409
+ */
410
+ declare function getVariantPrice(variant: Pick<ProductVariant, 'price' | 'salePrice'> | null | undefined, productBasePrice: string): number;
411
+ /**
412
+ * Calculate the total for a cart.
413
+ * Cart does NOT have a 'total' field - use this helper!
414
+ *
415
+ * @param cart - The cart object
416
+ * @param shippingPrice - Optional shipping price (string or number)
417
+ * @returns Object with subtotal, discount, shipping, and total
418
+ *
419
+ * @example
420
+ * ```typescript
421
+ * const cart = await omni.getCart(cartId);
422
+ * const totals = getCartTotals(cart);
423
+ * // totals = { subtotal: 59.98, discount: 10, shipping: 0, total: 49.98 }
424
+ *
425
+ * // With shipping
426
+ * const totals = getCartTotals(cart, selectedRate?.price);
427
+ * // totals = { subtotal: 59.98, discount: 0, shipping: 5.99, total: 65.97 }
428
+ * ```
429
+ */
430
+ declare function getCartTotals(cart: Pick<Cart, 'subtotal' | 'discountAmount'> | null | undefined, shippingPrice?: string | number | null): {
431
+ subtotal: number;
432
+ discount: number;
433
+ shipping: number;
434
+ total: number;
435
+ };
436
+ /**
437
+ * Get the display name for a cart item.
438
+ * Handles the nested product/variant structure - uses variant name if available,
439
+ * otherwise falls back to product name.
440
+ *
441
+ * @param item - The cart item
442
+ * @returns The display name for the item
443
+ *
444
+ * @example
445
+ * ```typescript
446
+ * cart.items.forEach(item => {
447
+ * const name = getCartItemName(item);
448
+ * console.log(`${name} x ${item.quantity}`);
449
+ * });
450
+ * ```
451
+ */
452
+ declare function getCartItemName(item: CartItem): string;
453
+ /**
454
+ * Get the image URL for a cart item.
455
+ * Checks variant image first, then falls back to the first product image.
456
+ *
457
+ * @param item - The cart item
458
+ * @returns The image URL or undefined if no image exists
459
+ *
460
+ * @example
461
+ * ```typescript
462
+ * const imageUrl = getCartItemImage(item);
463
+ * if (imageUrl) {
464
+ * return <img src={imageUrl} alt={getCartItemName(item)} />;
465
+ * }
466
+ * ```
467
+ */
468
+ declare function getCartItemImage(item: CartItem): string | undefined;
469
+ /**
470
+ * Convert variant attributes to an array of name/value pairs.
471
+ * Transforms { "Color": "Red", "Size": "M" } to [{ name: "Color", value: "Red" }, ...]
472
+ *
473
+ * @param variant - The product variant (or object with attributes)
474
+ * @returns Array of { name, value } objects
475
+ *
476
+ * @example
477
+ * ```typescript
478
+ * const options = getVariantOptions(selectedVariant);
479
+ * // options = [{ name: "Color", value: "Red" }, { name: "Size", value: "M" }]
480
+ *
481
+ * options.forEach(opt => {
482
+ * console.log(`${opt.name}: ${opt.value}`);
483
+ * });
484
+ * ```
485
+ */
486
+ declare function getVariantOptions(variant: Pick<ProductVariant, 'attributes'> | null | undefined): Array<{
487
+ name: string;
488
+ value: string;
489
+ }>;
331
490
  interface ProductQueryParams {
332
491
  page?: number;
333
492
  limit?: number;
@@ -419,10 +578,22 @@ interface UpdateProductDto {
419
578
  interface Order {
420
579
  id: string;
421
580
  externalId?: string;
581
+ /** Human-readable order number (e.g., "#1001") */
582
+ orderNumber?: string;
422
583
  status: OrderStatus;
423
- totalAmount: number;
424
- customer: OrderCustomer;
584
+ /** Total amount as string (e.g., "99.99"). Use parseFloat() for calculations. */
585
+ totalAmount: string;
586
+ /** Currency code (e.g., "USD", "ILS") */
587
+ currency?: string;
588
+ /** Customer info (may be null for guest orders) */
589
+ customer?: OrderCustomer | null;
425
590
  items: OrderItem[];
591
+ /** Number of items in the order */
592
+ itemCount?: number;
593
+ /** Shipping address for the order */
594
+ shippingAddress?: OrderAddress;
595
+ /** Billing address for the order */
596
+ billingAddress?: OrderAddress;
426
597
  platform?: string;
427
598
  createdAt: string;
428
599
  }
@@ -434,20 +605,32 @@ interface OrderCustomer {
434
605
  address?: OrderAddress;
435
606
  }
436
607
  interface OrderAddress {
608
+ firstName?: string;
609
+ lastName?: string;
610
+ company?: string;
437
611
  line1: string;
438
612
  line2?: string;
439
613
  city: string;
614
+ /** State/Province (alias: use `state` or `region`) */
440
615
  state?: string;
616
+ /** Region/Province (alias: use `state` or `region`) */
617
+ region?: string;
441
618
  postalCode: string;
442
619
  country: string;
620
+ phone?: string;
443
621
  }
444
622
  interface OrderItem {
445
623
  productId: string;
624
+ variantId?: string;
446
625
  sku?: string;
447
626
  name?: string;
448
627
  quantity: number;
628
+ /** Price per unit as number */
449
629
  price: number;
450
- variantId?: string;
630
+ /** Unit price as string (for consistency with Cart/Checkout) */
631
+ unitPrice?: string;
632
+ /** Product image URL */
633
+ image?: string;
451
634
  }
452
635
  interface OrderQueryParams {
453
636
  page?: number;
@@ -1995,6 +2178,11 @@ declare class OmniSyncClient {
1995
2178
  private readonly timeout;
1996
2179
  private customerToken;
1997
2180
  private customerCartId;
2181
+ /**
2182
+ * Virtual cart ID used for localStorage carts (guest users not logged in).
2183
+ * When a cart has this ID, operations use localStorage instead of server API.
2184
+ */
2185
+ private readonly VIRTUAL_LOCAL_CART_ID;
1998
2186
  private readonly onAuthError?;
1999
2187
  constructor(options: OmniSyncClientOptions);
2000
2188
  /**
@@ -3454,6 +3642,16 @@ declare class OmniSyncClient {
3454
3642
  * Save local cart to localStorage
3455
3643
  */
3456
3644
  private saveLocalCart;
3645
+ /**
3646
+ * Calculate subtotal for a local cart
3647
+ */
3648
+ private calculateLocalCartSubtotal;
3649
+ /**
3650
+ * Convert LocalCart to Cart format for consistent API.
3651
+ * This allows cart methods to return the same Cart type regardless
3652
+ * of whether data is stored locally or on the server.
3653
+ */
3654
+ private localCartToCart;
3457
3655
  /**
3458
3656
  * Add item to local cart (NO API call)
3459
3657
  * If item already exists, updates quantity
@@ -3923,4 +4121,4 @@ declare function isWebhookEventType(event: WebhookEvent, type: WebhookEventType)
3923
4121
  */
3924
4122
  declare function createWebhookHandler(handlers: Partial<Record<WebhookEventType, (event: WebhookEvent) => Promise<void>>>): (payload: unknown) => Promise<void>;
3925
4123
 
3926
- export { type AddToCartDto, type AppliedDiscount, type ApplyCouponDto, type BulkInventoryResponse, type BulkSaveVariantsDto, type BulkSaveVariantsResponse, type BulkVariantInput, type Cart, type CartItem, type CartStatus, type CategorySuggestion, type Checkout, type CheckoutAddress, type CheckoutLineItem, type CheckoutPrefillData, type CheckoutStatus, type CompleteCheckoutResponse, type CompleteDraftDto, type ConnectorPlatform, type Coupon, type CouponCreateResponse, type CouponQueryParams, type CouponStatus, type CouponType, type CouponValidationWarning, type CreateAddressDto, type CreateCheckoutDto, type CreateCouponDto, type CreateCustomApiDto, type CreateCustomerDto, type CreateGuestOrderDto, type CreateOrderDto, type CreateProductDto, type CreateRefundDto, type CreateVariantDto, type CustomApiAuthType, type CustomApiConnectionStatus, type CustomApiCredentials, type CustomApiIntegration, type CustomApiSyncConfig, type CustomApiSyncDirection, type CustomApiTestResult, type Customer, type CustomerAddress, type CustomerAuthResponse, type CustomerOAuthProvider, type CustomerProfile, type CustomerQueryParams, type DraftLineItem, type EditInventoryDto, type EmailVerificationResponse, type FormatPriceOptions, type FulfillOrderDto, type GuestCheckoutStartResponse, type GuestOrderResponse, type InsufficientStockError, type InventoryInfo, type InventorySyncStatus, type LocalCart, type LocalCartItem, type MergeCartsDto, type OAuthAuthorizeResponse, type OAuthCallbackResponse, type OAuthConnection, type OAuthConnectionsResponse, type OAuthProvidersResponse, type OmniSyncApiError, OmniSyncClient, type OmniSyncClientOptions, OmniSyncError, type Order, type OrderAddress, type OrderCustomer, type OrderItem, type OrderQueryParams, type OrderStatus, type PaginatedResponse, type PaymentConfig, type PaymentIntent, type PaymentProviderConfig, type PaymentProvidersConfig, type PaymentStatus, type PlatformCouponCapabilities, type Product, type ProductAttributeInput, type ProductImage, type ProductQueryParams, type ProductSuggestion, type ProductVariant, type PublishProductResponse, type ReconcileInventoryResponse, type Refund, type RefundLineItem, type RefundLineItemResponse, type RefundType, type RegisterCustomerDto, type SearchSuggestions, type SelectShippingMethodDto, type SendInvoiceDto, type SetBillingAddressDto, type SetCheckoutCustomerDto, type SetShippingAddressDto, type SetShippingAddressResponse, type ShippingLine, type ShippingRate, type StockAvailabilityRequest, type StockAvailabilityResponse, type StockAvailabilityResult, type StoreInfo, type SyncJob, type UpdateAddressDto, type UpdateCartItemDto, type UpdateCouponDto, type UpdateCustomApiDto, type UpdateCustomerDto, type UpdateDraftDto, type UpdateInventoryDto, type UpdateOrderDto, type UpdateOrderShippingDto, type UpdateProductDto, type UpdateVariantDto, type UpdateVariantInventoryDto, type VariantInventoryResponse, type VariantPlatformOverlay, type VariantStatus, type WaitForOrderOptions, type WaitForOrderResult, type WebhookEvent, type WebhookEventType, createWebhookHandler, formatPrice, getDescriptionContent, formatPrice as getPriceDisplay, getStockStatus, isCouponApplicableToProduct, isHtmlDescription, isWebhookEventType, parseWebhookEvent, verifyWebhook };
4124
+ export { type AddToCartDto, type AppliedDiscount, type ApplyCouponDto, type BulkInventoryResponse, type BulkSaveVariantsDto, type BulkSaveVariantsResponse, type BulkVariantInput, type Cart, type CartItem, type CartStatus, type CategorySuggestion, type Checkout, type CheckoutAddress, type CheckoutLineItem, type CheckoutPrefillData, type CheckoutStatus, type CompleteCheckoutResponse, type CompleteDraftDto, type ConnectorPlatform, type Coupon, type CouponCreateResponse, type CouponQueryParams, type CouponStatus, type CouponType, type CouponValidationWarning, type CreateAddressDto, type CreateCheckoutDto, type CreateCouponDto, type CreateCustomApiDto, type CreateCustomerDto, type CreateGuestOrderDto, type CreateOrderDto, type CreateProductDto, type CreateRefundDto, type CreateVariantDto, type CustomApiAuthType, type CustomApiConnectionStatus, type CustomApiCredentials, type CustomApiIntegration, type CustomApiSyncConfig, type CustomApiSyncDirection, type CustomApiTestResult, type Customer, type CustomerAddress, type CustomerAuthResponse, type CustomerOAuthProvider, type CustomerProfile, type CustomerQueryParams, type DraftLineItem, type EditInventoryDto, type EmailVerificationResponse, type ExtendReservationResponse, type FormatPriceOptions, type FulfillOrderDto, type GuestCheckoutStartResponse, type GuestOrderResponse, type InsufficientStockError, type InventoryInfo, type InventoryReservationStrategy, type InventorySyncStatus, type InventoryTrackingMode, type LocalCart, type LocalCartItem, type MergeCartsDto, type OAuthAuthorizeResponse, type OAuthCallbackResponse, type OAuthConnection, type OAuthConnectionsResponse, type OAuthProvidersResponse, type OmniSyncApiError, OmniSyncClient, type OmniSyncClientOptions, OmniSyncError, type Order, type OrderAddress, type OrderCustomer, type OrderItem, type OrderQueryParams, type OrderStatus, type PaginatedResponse, type PaymentConfig, type PaymentIntent, type PaymentProviderConfig, type PaymentProvidersConfig, type PaymentStatus, type PlatformCouponCapabilities, type Product, type ProductAttributeInput, type ProductAvailability, type ProductImage, type ProductMetafield, type ProductQueryParams, type ProductSuggestion, type ProductVariant, type PublishProductResponse, type ReconcileInventoryResponse, type Refund, type RefundLineItem, type RefundLineItemResponse, type RefundType, type RegisterCustomerDto, type ReservationInfo, type SearchSuggestions, type SelectShippingMethodDto, type SendInvoiceDto, type SetBillingAddressDto, type SetCheckoutCustomerDto, type SetShippingAddressDto, type SetShippingAddressResponse, type ShippingLine, type ShippingRate, type StockAvailabilityRequest, type StockAvailabilityResponse, type StockAvailabilityResult, type StoreInfo, type SyncJob, type TaxBreakdown, type TaxBreakdownItem, type UpdateAddressDto, type UpdateCartItemDto, type UpdateCouponDto, type UpdateCustomApiDto, type UpdateCustomerDto, type UpdateDraftDto, type UpdateInventoryDto, type UpdateOrderDto, type UpdateOrderShippingDto, type UpdateProductDto, type UpdateVariantDto, type UpdateVariantInventoryDto, type VariantInventoryResponse, type VariantPlatformOverlay, type VariantStatus, type WaitForOrderOptions, type WaitForOrderResult, type WebhookEvent, type WebhookEventType, createWebhookHandler, formatPrice, getCartItemImage, getCartItemName, getCartTotals, getDescriptionContent, formatPrice as getPriceDisplay, getProductPrice, getProductPriceInfo, getStockStatus, getVariantOptions, getVariantPrice, isCouponApplicableToProduct, isHtmlDescription, isWebhookEventType, parseWebhookEvent, verifyWebhook };
package/dist/index.js CHANGED
@@ -24,9 +24,16 @@ __export(index_exports, {
24
24
  OmniSyncError: () => OmniSyncError,
25
25
  createWebhookHandler: () => createWebhookHandler,
26
26
  formatPrice: () => formatPrice,
27
+ getCartItemImage: () => getCartItemImage,
28
+ getCartItemName: () => getCartItemName,
29
+ getCartTotals: () => getCartTotals,
27
30
  getDescriptionContent: () => getDescriptionContent,
28
31
  getPriceDisplay: () => formatPrice,
32
+ getProductPrice: () => getProductPrice,
33
+ getProductPriceInfo: () => getProductPriceInfo,
29
34
  getStockStatus: () => getStockStatus,
35
+ getVariantOptions: () => getVariantOptions,
36
+ getVariantPrice: () => getVariantPrice,
30
37
  isCouponApplicableToProduct: () => isCouponApplicableToProduct,
31
38
  isHtmlDescription: () => isHtmlDescription,
32
39
  isWebhookEventType: () => isWebhookEventType,
@@ -42,6 +49,11 @@ var OmniSyncClient = class {
42
49
  constructor(options) {
43
50
  this.customerToken = null;
44
51
  this.customerCartId = null;
52
+ /**
53
+ * Virtual cart ID used for localStorage carts (guest users not logged in).
54
+ * When a cart has this ID, operations use localStorage instead of server API.
55
+ */
56
+ this.VIRTUAL_LOCAL_CART_ID = "__local__";
45
57
  // -------------------- Local Cart (Client-Side for Guests) --------------------
46
58
  // These methods store cart data in localStorage - NO API calls!
47
59
  // Use for guest users in vibe-coded sites
@@ -1633,6 +1645,10 @@ var OmniSyncClient = class {
1633
1645
  * ```
1634
1646
  */
1635
1647
  async createCart() {
1648
+ if (this.isVibeCodedMode() && !this.isCustomerLoggedIn()) {
1649
+ const localCart = this.getLocalCart();
1650
+ return this.localCartToCart(localCart);
1651
+ }
1636
1652
  if (this.isVibeCodedMode()) {
1637
1653
  return this.vibeCodedRequest("POST", "/cart");
1638
1654
  }
@@ -1675,6 +1691,9 @@ var OmniSyncClient = class {
1675
1691
  * Get a cart by ID
1676
1692
  */
1677
1693
  async getCart(cartId) {
1694
+ if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
1695
+ return this.localCartToCart(this.getLocalCart());
1696
+ }
1678
1697
  if (this.isVibeCodedMode()) {
1679
1698
  return this.vibeCodedRequest("GET", `/cart/${cartId}`);
1680
1699
  }
@@ -1696,6 +1715,14 @@ var OmniSyncClient = class {
1696
1715
  * ```
1697
1716
  */
1698
1717
  async addToCart(cartId, item) {
1718
+ if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
1719
+ this.addToLocalCart({
1720
+ productId: item.productId,
1721
+ variantId: item.variantId,
1722
+ quantity: item.quantity
1723
+ });
1724
+ return this.localCartToCart(this.getLocalCart());
1725
+ }
1699
1726
  if (this.isVibeCodedMode()) {
1700
1727
  return this.vibeCodedRequest("POST", `/cart/${cartId}/items`, item);
1701
1728
  }
@@ -1713,6 +1740,15 @@ var OmniSyncClient = class {
1713
1740
  * ```
1714
1741
  */
1715
1742
  async updateCartItem(cartId, itemId, data) {
1743
+ if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
1744
+ const index = parseInt(itemId.replace("local_", ""), 10);
1745
+ const localCart = this.getLocalCart();
1746
+ if (index >= 0 && index < localCart.items.length) {
1747
+ const item = localCart.items[index];
1748
+ this.updateLocalCartItem(item.productId, data.quantity, item.variantId);
1749
+ }
1750
+ return this.localCartToCart(this.getLocalCart());
1751
+ }
1716
1752
  if (this.isVibeCodedMode()) {
1717
1753
  return this.vibeCodedRequest("PATCH", `/cart/${cartId}/items/${itemId}`, data);
1718
1754
  }
@@ -1730,6 +1766,15 @@ var OmniSyncClient = class {
1730
1766
  * ```
1731
1767
  */
1732
1768
  async removeCartItem(cartId, itemId) {
1769
+ if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
1770
+ const index = parseInt(itemId.replace("local_", ""), 10);
1771
+ const localCart = this.getLocalCart();
1772
+ if (index >= 0 && index < localCart.items.length) {
1773
+ const item = localCart.items[index];
1774
+ this.removeFromLocalCart(item.productId, item.variantId);
1775
+ }
1776
+ return this.localCartToCart(this.getLocalCart());
1777
+ }
1733
1778
  if (this.isVibeCodedMode()) {
1734
1779
  return this.vibeCodedRequest("DELETE", `/cart/${cartId}/items/${itemId}`);
1735
1780
  }
@@ -1747,6 +1792,10 @@ var OmniSyncClient = class {
1747
1792
  * ```
1748
1793
  */
1749
1794
  async clearCart(cartId) {
1795
+ if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
1796
+ this.clearLocalCart();
1797
+ return this.localCartToCart(this.getLocalCart());
1798
+ }
1750
1799
  if (this.isVibeCodedMode()) {
1751
1800
  return this.vibeCodedRequest("DELETE", `/cart/${cartId}`);
1752
1801
  }
@@ -2561,6 +2610,62 @@ var OmniSyncClient = class {
2561
2610
  cart.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2562
2611
  window.localStorage.setItem(this.LOCAL_CART_KEY, JSON.stringify(cart));
2563
2612
  }
2613
+ /**
2614
+ * Calculate subtotal for a local cart
2615
+ */
2616
+ calculateLocalCartSubtotal(localCart) {
2617
+ const total = localCart.items.reduce((sum, item) => {
2618
+ return sum + parseFloat(item.price || "0") * item.quantity;
2619
+ }, 0);
2620
+ return total.toFixed(2);
2621
+ }
2622
+ /**
2623
+ * Convert LocalCart to Cart format for consistent API.
2624
+ * This allows cart methods to return the same Cart type regardless
2625
+ * of whether data is stored locally or on the server.
2626
+ */
2627
+ localCartToCart(localCart) {
2628
+ return {
2629
+ id: this.VIRTUAL_LOCAL_CART_ID,
2630
+ sessionToken: null,
2631
+ customerId: null,
2632
+ status: "ACTIVE",
2633
+ currency: "USD",
2634
+ // Default currency
2635
+ notes: localCart.notes || null,
2636
+ subtotal: this.calculateLocalCartSubtotal(localCart),
2637
+ discountAmount: "0",
2638
+ couponCode: localCart.couponCode || null,
2639
+ items: localCart.items.map((item, index) => ({
2640
+ id: `local_${index}`,
2641
+ productId: item.productId,
2642
+ variantId: item.variantId || null,
2643
+ quantity: item.quantity,
2644
+ unitPrice: item.price || "0",
2645
+ discountAmount: "0",
2646
+ notes: null,
2647
+ metadata: null,
2648
+ product: {
2649
+ id: item.productId,
2650
+ name: item.name || "Unknown Product",
2651
+ sku: item.sku || "",
2652
+ images: item.image ? [{ url: item.image }] : []
2653
+ },
2654
+ variant: item.variantId ? {
2655
+ id: item.variantId,
2656
+ name: null,
2657
+ sku: null,
2658
+ image: null
2659
+ } : null,
2660
+ createdAt: item.addedAt,
2661
+ updatedAt: item.addedAt
2662
+ })),
2663
+ itemCount: localCart.items.reduce((sum, i) => sum + i.quantity, 0),
2664
+ expiresAt: null,
2665
+ createdAt: localCart.updatedAt,
2666
+ updatedAt: localCart.updatedAt
2667
+ };
2668
+ }
2564
2669
  /**
2565
2670
  * Add item to local cart (NO API call)
2566
2671
  * If item already exists, updates quantity
@@ -3421,6 +3526,75 @@ function formatPrice(priceString, options) {
3421
3526
  currency
3422
3527
  }).format(isNaN(value) ? 0 : value);
3423
3528
  }
3529
+ function getProductPrice(product) {
3530
+ if (!product) return 0;
3531
+ const basePrice = parseFloat(product.basePrice) || 0;
3532
+ const salePrice = product.salePrice ? parseFloat(product.salePrice) : null;
3533
+ if (salePrice !== null && salePrice < basePrice) {
3534
+ return salePrice;
3535
+ }
3536
+ return basePrice;
3537
+ }
3538
+ function getProductPriceInfo(product) {
3539
+ if (!product) {
3540
+ return { price: 0, originalPrice: 0, isOnSale: false, discountAmount: 0, discountPercent: 0 };
3541
+ }
3542
+ const basePrice = parseFloat(product.basePrice) || 0;
3543
+ const salePrice = product.salePrice ? parseFloat(product.salePrice) : null;
3544
+ const isOnSale = salePrice !== null && salePrice < basePrice;
3545
+ const effectivePrice = isOnSale ? salePrice : basePrice;
3546
+ const discountAmount = isOnSale ? basePrice - salePrice : 0;
3547
+ const discountPercent = isOnSale && basePrice > 0 ? Math.round(discountAmount / basePrice * 100) : 0;
3548
+ return {
3549
+ price: effectivePrice,
3550
+ originalPrice: basePrice,
3551
+ isOnSale,
3552
+ discountAmount,
3553
+ discountPercent
3554
+ };
3555
+ }
3556
+ function getVariantPrice(variant, productBasePrice) {
3557
+ if (!variant) return parseFloat(productBasePrice) || 0;
3558
+ const variantPrice = variant.price ? parseFloat(variant.price) : null;
3559
+ const variantSalePrice = variant.salePrice ? parseFloat(variant.salePrice) : null;
3560
+ const basePrice = parseFloat(productBasePrice) || 0;
3561
+ if (variantSalePrice !== null && variantPrice !== null && variantSalePrice < variantPrice) {
3562
+ return variantSalePrice;
3563
+ }
3564
+ if (variantPrice !== null) {
3565
+ return variantPrice;
3566
+ }
3567
+ return basePrice;
3568
+ }
3569
+ function getCartTotals(cart, shippingPrice) {
3570
+ if (!cart) {
3571
+ return { subtotal: 0, discount: 0, shipping: 0, total: 0 };
3572
+ }
3573
+ const subtotal = parseFloat(cart.subtotal) || 0;
3574
+ const discount = parseFloat(cart.discountAmount) || 0;
3575
+ const shipping = typeof shippingPrice === "string" ? parseFloat(shippingPrice) || 0 : shippingPrice || 0;
3576
+ const total = subtotal - discount + shipping;
3577
+ return { subtotal, discount, shipping, total };
3578
+ }
3579
+ function getCartItemName(item) {
3580
+ return item.variant?.name || item.product.name;
3581
+ }
3582
+ function getCartItemImage(item) {
3583
+ if (item.variant?.image) {
3584
+ if (typeof item.variant.image === "string") {
3585
+ return item.variant.image;
3586
+ }
3587
+ if (typeof item.variant.image === "object" && item.variant.image !== null) {
3588
+ const imgObj = item.variant.image;
3589
+ if (imgObj.url) return imgObj.url;
3590
+ }
3591
+ }
3592
+ return item.product.images?.[0]?.url;
3593
+ }
3594
+ function getVariantOptions(variant) {
3595
+ if (!variant?.attributes) return [];
3596
+ return Object.entries(variant.attributes).filter(([, value]) => typeof value === "string").map(([name, value]) => ({ name, value }));
3597
+ }
3424
3598
  function isCouponApplicableToProduct(coupon, productId, productCategoryIds) {
3425
3599
  if (coupon.excludedProducts?.includes(productId)) {
3426
3600
  return false;
@@ -3446,9 +3620,16 @@ function isCouponApplicableToProduct(coupon, productId, productCategoryIds) {
3446
3620
  OmniSyncError,
3447
3621
  createWebhookHandler,
3448
3622
  formatPrice,
3623
+ getCartItemImage,
3624
+ getCartItemName,
3625
+ getCartTotals,
3449
3626
  getDescriptionContent,
3450
3627
  getPriceDisplay,
3628
+ getProductPrice,
3629
+ getProductPriceInfo,
3451
3630
  getStockStatus,
3631
+ getVariantOptions,
3632
+ getVariantPrice,
3452
3633
  isCouponApplicableToProduct,
3453
3634
  isHtmlDescription,
3454
3635
  isWebhookEventType,
package/dist/index.mjs CHANGED
@@ -12,6 +12,11 @@ var OmniSyncClient = class {
12
12
  constructor(options) {
13
13
  this.customerToken = null;
14
14
  this.customerCartId = null;
15
+ /**
16
+ * Virtual cart ID used for localStorage carts (guest users not logged in).
17
+ * When a cart has this ID, operations use localStorage instead of server API.
18
+ */
19
+ this.VIRTUAL_LOCAL_CART_ID = "__local__";
15
20
  // -------------------- Local Cart (Client-Side for Guests) --------------------
16
21
  // These methods store cart data in localStorage - NO API calls!
17
22
  // Use for guest users in vibe-coded sites
@@ -1603,6 +1608,10 @@ var OmniSyncClient = class {
1603
1608
  * ```
1604
1609
  */
1605
1610
  async createCart() {
1611
+ if (this.isVibeCodedMode() && !this.isCustomerLoggedIn()) {
1612
+ const localCart = this.getLocalCart();
1613
+ return this.localCartToCart(localCart);
1614
+ }
1606
1615
  if (this.isVibeCodedMode()) {
1607
1616
  return this.vibeCodedRequest("POST", "/cart");
1608
1617
  }
@@ -1645,6 +1654,9 @@ var OmniSyncClient = class {
1645
1654
  * Get a cart by ID
1646
1655
  */
1647
1656
  async getCart(cartId) {
1657
+ if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
1658
+ return this.localCartToCart(this.getLocalCart());
1659
+ }
1648
1660
  if (this.isVibeCodedMode()) {
1649
1661
  return this.vibeCodedRequest("GET", `/cart/${cartId}`);
1650
1662
  }
@@ -1666,6 +1678,14 @@ var OmniSyncClient = class {
1666
1678
  * ```
1667
1679
  */
1668
1680
  async addToCart(cartId, item) {
1681
+ if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
1682
+ this.addToLocalCart({
1683
+ productId: item.productId,
1684
+ variantId: item.variantId,
1685
+ quantity: item.quantity
1686
+ });
1687
+ return this.localCartToCart(this.getLocalCart());
1688
+ }
1669
1689
  if (this.isVibeCodedMode()) {
1670
1690
  return this.vibeCodedRequest("POST", `/cart/${cartId}/items`, item);
1671
1691
  }
@@ -1683,6 +1703,15 @@ var OmniSyncClient = class {
1683
1703
  * ```
1684
1704
  */
1685
1705
  async updateCartItem(cartId, itemId, data) {
1706
+ if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
1707
+ const index = parseInt(itemId.replace("local_", ""), 10);
1708
+ const localCart = this.getLocalCart();
1709
+ if (index >= 0 && index < localCart.items.length) {
1710
+ const item = localCart.items[index];
1711
+ this.updateLocalCartItem(item.productId, data.quantity, item.variantId);
1712
+ }
1713
+ return this.localCartToCart(this.getLocalCart());
1714
+ }
1686
1715
  if (this.isVibeCodedMode()) {
1687
1716
  return this.vibeCodedRequest("PATCH", `/cart/${cartId}/items/${itemId}`, data);
1688
1717
  }
@@ -1700,6 +1729,15 @@ var OmniSyncClient = class {
1700
1729
  * ```
1701
1730
  */
1702
1731
  async removeCartItem(cartId, itemId) {
1732
+ if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
1733
+ const index = parseInt(itemId.replace("local_", ""), 10);
1734
+ const localCart = this.getLocalCart();
1735
+ if (index >= 0 && index < localCart.items.length) {
1736
+ const item = localCart.items[index];
1737
+ this.removeFromLocalCart(item.productId, item.variantId);
1738
+ }
1739
+ return this.localCartToCart(this.getLocalCart());
1740
+ }
1703
1741
  if (this.isVibeCodedMode()) {
1704
1742
  return this.vibeCodedRequest("DELETE", `/cart/${cartId}/items/${itemId}`);
1705
1743
  }
@@ -1717,6 +1755,10 @@ var OmniSyncClient = class {
1717
1755
  * ```
1718
1756
  */
1719
1757
  async clearCart(cartId) {
1758
+ if (cartId === this.VIRTUAL_LOCAL_CART_ID) {
1759
+ this.clearLocalCart();
1760
+ return this.localCartToCart(this.getLocalCart());
1761
+ }
1720
1762
  if (this.isVibeCodedMode()) {
1721
1763
  return this.vibeCodedRequest("DELETE", `/cart/${cartId}`);
1722
1764
  }
@@ -2531,6 +2573,62 @@ var OmniSyncClient = class {
2531
2573
  cart.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2532
2574
  window.localStorage.setItem(this.LOCAL_CART_KEY, JSON.stringify(cart));
2533
2575
  }
2576
+ /**
2577
+ * Calculate subtotal for a local cart
2578
+ */
2579
+ calculateLocalCartSubtotal(localCart) {
2580
+ const total = localCart.items.reduce((sum, item) => {
2581
+ return sum + parseFloat(item.price || "0") * item.quantity;
2582
+ }, 0);
2583
+ return total.toFixed(2);
2584
+ }
2585
+ /**
2586
+ * Convert LocalCart to Cart format for consistent API.
2587
+ * This allows cart methods to return the same Cart type regardless
2588
+ * of whether data is stored locally or on the server.
2589
+ */
2590
+ localCartToCart(localCart) {
2591
+ return {
2592
+ id: this.VIRTUAL_LOCAL_CART_ID,
2593
+ sessionToken: null,
2594
+ customerId: null,
2595
+ status: "ACTIVE",
2596
+ currency: "USD",
2597
+ // Default currency
2598
+ notes: localCart.notes || null,
2599
+ subtotal: this.calculateLocalCartSubtotal(localCart),
2600
+ discountAmount: "0",
2601
+ couponCode: localCart.couponCode || null,
2602
+ items: localCart.items.map((item, index) => ({
2603
+ id: `local_${index}`,
2604
+ productId: item.productId,
2605
+ variantId: item.variantId || null,
2606
+ quantity: item.quantity,
2607
+ unitPrice: item.price || "0",
2608
+ discountAmount: "0",
2609
+ notes: null,
2610
+ metadata: null,
2611
+ product: {
2612
+ id: item.productId,
2613
+ name: item.name || "Unknown Product",
2614
+ sku: item.sku || "",
2615
+ images: item.image ? [{ url: item.image }] : []
2616
+ },
2617
+ variant: item.variantId ? {
2618
+ id: item.variantId,
2619
+ name: null,
2620
+ sku: null,
2621
+ image: null
2622
+ } : null,
2623
+ createdAt: item.addedAt,
2624
+ updatedAt: item.addedAt
2625
+ })),
2626
+ itemCount: localCart.items.reduce((sum, i) => sum + i.quantity, 0),
2627
+ expiresAt: null,
2628
+ createdAt: localCart.updatedAt,
2629
+ updatedAt: localCart.updatedAt
2630
+ };
2631
+ }
2534
2632
  /**
2535
2633
  * Add item to local cart (NO API call)
2536
2634
  * If item already exists, updates quantity
@@ -3391,6 +3489,75 @@ function formatPrice(priceString, options) {
3391
3489
  currency
3392
3490
  }).format(isNaN(value) ? 0 : value);
3393
3491
  }
3492
+ function getProductPrice(product) {
3493
+ if (!product) return 0;
3494
+ const basePrice = parseFloat(product.basePrice) || 0;
3495
+ const salePrice = product.salePrice ? parseFloat(product.salePrice) : null;
3496
+ if (salePrice !== null && salePrice < basePrice) {
3497
+ return salePrice;
3498
+ }
3499
+ return basePrice;
3500
+ }
3501
+ function getProductPriceInfo(product) {
3502
+ if (!product) {
3503
+ return { price: 0, originalPrice: 0, isOnSale: false, discountAmount: 0, discountPercent: 0 };
3504
+ }
3505
+ const basePrice = parseFloat(product.basePrice) || 0;
3506
+ const salePrice = product.salePrice ? parseFloat(product.salePrice) : null;
3507
+ const isOnSale = salePrice !== null && salePrice < basePrice;
3508
+ const effectivePrice = isOnSale ? salePrice : basePrice;
3509
+ const discountAmount = isOnSale ? basePrice - salePrice : 0;
3510
+ const discountPercent = isOnSale && basePrice > 0 ? Math.round(discountAmount / basePrice * 100) : 0;
3511
+ return {
3512
+ price: effectivePrice,
3513
+ originalPrice: basePrice,
3514
+ isOnSale,
3515
+ discountAmount,
3516
+ discountPercent
3517
+ };
3518
+ }
3519
+ function getVariantPrice(variant, productBasePrice) {
3520
+ if (!variant) return parseFloat(productBasePrice) || 0;
3521
+ const variantPrice = variant.price ? parseFloat(variant.price) : null;
3522
+ const variantSalePrice = variant.salePrice ? parseFloat(variant.salePrice) : null;
3523
+ const basePrice = parseFloat(productBasePrice) || 0;
3524
+ if (variantSalePrice !== null && variantPrice !== null && variantSalePrice < variantPrice) {
3525
+ return variantSalePrice;
3526
+ }
3527
+ if (variantPrice !== null) {
3528
+ return variantPrice;
3529
+ }
3530
+ return basePrice;
3531
+ }
3532
+ function getCartTotals(cart, shippingPrice) {
3533
+ if (!cart) {
3534
+ return { subtotal: 0, discount: 0, shipping: 0, total: 0 };
3535
+ }
3536
+ const subtotal = parseFloat(cart.subtotal) || 0;
3537
+ const discount = parseFloat(cart.discountAmount) || 0;
3538
+ const shipping = typeof shippingPrice === "string" ? parseFloat(shippingPrice) || 0 : shippingPrice || 0;
3539
+ const total = subtotal - discount + shipping;
3540
+ return { subtotal, discount, shipping, total };
3541
+ }
3542
+ function getCartItemName(item) {
3543
+ return item.variant?.name || item.product.name;
3544
+ }
3545
+ function getCartItemImage(item) {
3546
+ if (item.variant?.image) {
3547
+ if (typeof item.variant.image === "string") {
3548
+ return item.variant.image;
3549
+ }
3550
+ if (typeof item.variant.image === "object" && item.variant.image !== null) {
3551
+ const imgObj = item.variant.image;
3552
+ if (imgObj.url) return imgObj.url;
3553
+ }
3554
+ }
3555
+ return item.product.images?.[0]?.url;
3556
+ }
3557
+ function getVariantOptions(variant) {
3558
+ if (!variant?.attributes) return [];
3559
+ return Object.entries(variant.attributes).filter(([, value]) => typeof value === "string").map(([name, value]) => ({ name, value }));
3560
+ }
3394
3561
  function isCouponApplicableToProduct(coupon, productId, productCategoryIds) {
3395
3562
  if (coupon.excludedProducts?.includes(productId)) {
3396
3563
  return false;
@@ -3415,9 +3582,16 @@ export {
3415
3582
  OmniSyncError,
3416
3583
  createWebhookHandler,
3417
3584
  formatPrice,
3585
+ getCartItemImage,
3586
+ getCartItemName,
3587
+ getCartTotals,
3418
3588
  getDescriptionContent,
3419
3589
  formatPrice as getPriceDisplay,
3590
+ getProductPrice,
3591
+ getProductPriceInfo,
3420
3592
  getStockStatus,
3593
+ getVariantOptions,
3594
+ getVariantPrice,
3421
3595
  isCouponApplicableToProduct,
3422
3596
  isHtmlDescription,
3423
3597
  isWebhookEventType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omni-sync-sdk",
3
- "version": "0.16.2",
3
+ "version": "0.17.2",
4
4
  "description": "Official SDK for building e-commerce storefronts with OmniSync Platform. Perfect for vibe-coded sites, AI-built stores (Cursor, Lovable, v0), and custom storefronts.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -16,6 +16,14 @@
16
16
  "dist",
17
17
  "README.md"
18
18
  ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format cjs,esm --dts",
21
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
22
+ "lint": "eslint \"src/**/*.ts\"",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "prepublishOnly": "pnpm build"
26
+ },
19
27
  "keywords": [
20
28
  "omni-sync",
21
29
  "e-commerce",
@@ -64,12 +72,5 @@
64
72
  "typescript": {
65
73
  "optional": true
66
74
  }
67
- },
68
- "scripts": {
69
- "build": "tsup src/index.ts --format cjs,esm --dts",
70
- "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
71
- "lint": "eslint \"src/**/*.ts\"",
72
- "test": "vitest run",
73
- "test:watch": "vitest"
74
75
  }
75
- }
76
+ }
package/LICENSE DELETED
File without changes