npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mantaray-digital/store-sdk

v1.1.1

Published

TypeScript SDK for building custom storefronts with Mantaray e-commerce backend

Downloads

273

Readme

@mantaray-digital/store-sdk

TypeScript SDK for building custom storefronts with Mantaray e-commerce backend. This SDK allows you to create fully customizable frontend experiences that integrate seamlessly with your Mantaray store.

📋 Table of Contents


🚀 Getting Started

For Store Admins

  1. Sign up for Mantaray Dashboard

  2. Configure Your Store

    • Add products, categories, collections
    • Set up shipping tiers
    • Configure payment methods
    • Customize store theme and settings
  3. Get Your API Credentials

    • Navigate to Settings → API Keys
    • Generate a new API key
    • Copy your Convex deployment URL

For Frontend Developers

Once you have your API credentials, you can start building your custom storefront using this SDK.


🔐 Authentication

The SDK requires two credentials:

| Credential | Description | Where to Find | |------------|-------------|---------------| | MANTARAY_API_KEY | Your store's API key | Dashboard → Settings → API Keys | | CONVEX_URL | Your Convex deployment URL | Dashboard → Settings → API Keys |

Setting Up Credentials

Option 1: Environment Variables (Recommended)

Create a .env file in your project:

MANTARAY_API_KEY=mk_live_xxxxxxxxxxxxxxxxxxxxxxxxx
CONVEX_URL=https://your-deployment.convex.cloud

Option 2: Pass Directly

const store = new MantarayStore({
  apiKey: 'mk_live_xxxxxxxxxxxxxxxxxxxxxxxxx',
  convexUrl: 'https://your-deployment.convex.cloud',
});

📦 Installation

npm install @mantaray-digital/store-sdk
# or
yarn add @mantaray-digital/store-sdk
# or
pnpm add @mantaray-digital/store-sdk

⚡ Quick Start

import { MantarayStore } from '@mantaray-digital/store-sdk';

// Initialize with environment variables
const store = new MantarayStore();

// Fetch products
const { products } = await store.products.list();

// Display products
products.forEach(product => {
  console.log(product.name, product.basePrice);
});

📚 API Reference

Store Module

Get store configuration and settings.

const config = await store.store.getConfig();
// Returns: StoreConfig with theme, settings, currency, etc.

Important: Always fetch store config on app initialization to:

  • Apply the correct currency and currency position
  • Use the store's theme colors
  • Display store information correctly

Products Module

List Products

// Get all products
const { products, total, hasMore } = await store.products.list();

// Filter by category
const { products } = await store.products.list({
  categoryId: 'category_id_here'
});

// Sort products
const { products } = await store.products.list({
  sortBy: 'price_asc' // or 'price_desc', 'name_asc', 'newest'
});

// Filter by price range
const { products } = await store.products.list({
  minPrice: 100,
  maxPrice: 500
});

// Only in-stock products
const { products } = await store.products.list({
  inStockOnly: true
});

Get Product Details

const product = await store.products.get('product_id_here');

Product Structure:

{
  _id: string;
  name: string;
  nameAr?: string;           // Arabic name (if available)
  description: string;
  descriptionAr?: string;    // Arabic description (if available)
  basePrice: number;         // Always in store's currency
  compareAtPrice?: number;   // Original price (if on sale)
  images: string[];          // Array of image URLs
  hasVariants: boolean;      // Whether product has variants
  options?: ProductOption[]; // Size, color, etc.
  variants?: ProductVariant[];
  stock?: number;
  isPreOrderEnabled?: boolean;
  specifications?: Record<string, string | number | boolean | string[]>;
  trustSignals?: TrustSignals;
  fitInfo?: FitInfo;
}

Design Note: Always display:

  • name or nameAr based on user's locale
  • basePrice formatted with store's currency symbol and position
  • compareAtPrice if available (show as strikethrough)
  • All images from the images array
  • Stock status if stock is provided

Categories Module

// Get all categories
const categories = await store.categories.list();

// Get category tree (nested structure)
const tree = await store.categories.getTree();

// Get single category
const category = await store.categories.get('category_id_here');

Category Structure:

{
  _id: string;
  name: string;
  nameAr?: string;
  slug: string;
  description?: string;
  descriptionAr?: string;
  imageUrl: string | null;
  productCount: number;
  sortOrder: number;
}

Collections Module

// Get all collections
const collections = await store.collections.list();

// Get collection with products
const collection = await store.collections.getWithProducts('collection_id_here');

Collection Structure:

{
  _id: string;
  name: string;
  nameAr?: string;
  slug: string;
  description?: string;
  descriptionAr?: string;
  imageUrl: string | null;
  isFeatured: boolean;
  sortOrder: number;
}

Cart Module

// Get current cart
const cart = await store.cart.get();

// Add item to cart
await store.cart.addItem({
  productId: 'product_id_here',
  variantId: 'variant_id_here', // Required if product has variants
  quantity: 1
});

// Update item quantity
await store.cart.updateItem('cart_item_id_here', 2);

// Remove item from cart
await store.cart.removeItem('cart_item_id_here');

// Clear cart
await store.cart.clear();

Cart Structure:

{
  _id?: string;
  items: CartItem[];
  subtotal: number;      // Always in store's currency
  itemCount: number;     // Total number of items
}

Design Note: Display cart with:

  • Product images, names, and prices
  • Quantity controls
  • Subtotal calculation
  • Currency formatting from store config

Checkout Module

// Get available shipping tiers
const shippingTiers = await store.checkout.getShippingTiers();

// Create order
const order = await store.checkout.createOrder({
  customerId: 'customer_id_here',
  shippingAddress: {
    name: 'John Doe',
    phone: '+1234567890',
    addressLine1: '123 Main St',
    addressLine2: 'Apt 4B',
    city: 'New York',
    postalCode: '10001'
  },
  shippingTierId: 'shipping_tier_id_here',
  notes: 'Special instructions'
});

Shipping Tier Structure:

{
  _id: string;
  name: string;
  nameAr?: string;
  description?: string;
  descriptionAr?: string;
  cost: number;              // Shipping cost in store's currency
  freeAbove?: number;        // Free shipping above this amount
  estimatedDays?: string;    // e.g., "2-3 business days"
  estimatedDaysAr?: string;
}

Design Note for Checkout Page:

  1. Display shipping options with:

    • Tier name and description
    • Cost (or "Free" if freeAbove is met)
    • Estimated delivery time
    • Allow user to select one tier
  2. Order summary should show:

    • Cart items with quantities
    • Subtotal
    • Shipping cost
    • Total amount
    • All in store's currency
  3. Address form should collect:

    • Full name
    • Phone number
    • Complete address (line 1, line 2, city, postal code)

Customer Module

// Register new customer
const result = await store.customer.register({
  email: '[email protected]',
  password: 'secure_password',
  name: 'John Doe',
  phone: '+1234567890'
});

// Login
const loginResult = await store.customer.login({
  email: '[email protected]',
  password: 'secure_password'
});

// Get customer profile
const profile = await store.customer.getProfile();

// Update customer profile
await store.customer.updateProfile({
  name: 'Jane Doe',
  phone: '+0987654321'
});

After Login:

// Link customer to cart, wishlist, etc.
store.setCustomer(loginResult.customerId);

Wishlist Module

// Get wishlist
const wishlist = await store.wishlist.get();

// Add to wishlist
await store.wishlist.add('product_id_here');

// Remove from wishlist
await store.wishlist.remove('wishlist_item_id_here');

// Check if product is in wishlist
const isInWishlist = await store.wishlist.has('product_id_here');

Analytics Module

Track user events for analytics:

// Track page view
await store.analytics.track('page_view', {
  path: '/products/123'
});

// Track product view
await store.analytics.track('product_view', {
  productId: 'product_id_here'
});

// Track add to cart
await store.analytics.track('add_to_cart', {
  productId: 'product_id_here',
  variantId: 'variant_id_here',
  quantity: 1
});

// Track checkout started
await store.analytics.track('checkout_started', {
  cartId: 'cart_id_here'
});

// Track checkout completed
await store.analytics.track('checkout_completed', {
  orderId: 'order_id_here',
  total: 100.50
});

🎨 Building Your Storefront

Step 1: Initialize the SDK

import { MantarayStore } from '@mantaray-digital/store-sdk';

const store = new MantarayStore();

Step 2: Fetch Store Configuration

const config = await store.store.getConfig();

// Apply theme to your app
document.documentElement.style.setProperty('--primary-color', config.theme.primaryColor);
document.documentElement.style.setProperty('--secondary-color', config.theme.secondaryColor);

Step 3: Create a Product List Page

async function loadProducts() {
  const { products } = await store.products.list();

  return products.map(product => ({
    id: product._id,
    name: product.name,
    price: formatPrice(product.basePrice, config.settings),
    image: product.images[0],
    inStock: product.stock > 0
  }));
}

function formatPrice(amount: number, settings: StoreSettings) {
  const symbol = settings.currencySymbol;
  const position = settings.currencyPosition;
  
  return position === 'before'
    ? `${symbol}${amount.toFixed(2)}`
    : `${amount.toFixed(2)}${symbol}`;
}

Step 4: Create a Product Detail Page

async function loadProduct(productId: string) {
  const product = await store.products.get(productId);

  return {
    id: product._id,
    name: product.name,
    description: product.description,
    price: product.basePrice,
    compareAtPrice: product.compareAtPrice,
    images: product.images,
    hasVariants: product.hasVariants,
    variants: product.variants,
    stock: product.stock,
    specifications: product.specifications,
    trustSignals: product.trustSignals
  };
}

Step 5: Create a Cart Page

async function loadCart() {
  const cart = await store.cart.get();

  return {
    items: cart.items.map(item => ({
      id: item.productId,
      name: item.name,
      price: item.price,
      quantity: item.quantity,
      image: item.imageUrl
    })),
    subtotal: cart.subtotal,
    itemCount: cart.itemCount
  };
}

Step 6: Create a Checkout Page

async function loadCheckout() {
  const [cart, shippingTiers] = await Promise.all([
    store.cart.get(),
    store.checkout.getShippingTiers()
  ]);

  return {
    cart,
    shippingTiers
  };
}

async function placeOrder(data: CreateOrderData) {
  const order = await store.checkout.createOrder(data);
  return order;
}

🎯 Design Guidelines

Currency Formatting

Always use the store's currency settings:

const config = await store.store.getConfig();

function formatPrice(amount: number) {
  const { currencySymbol, currencyPosition } = config.settings;
  
  if (currencyPosition === 'before') {
    return `${currencySymbol}${amount.toFixed(2)}`;
  } else {
    return `${amount.toFixed(2)}${currencySymbol}`;
  }
}

Localization

Support both English and Arabic:

function getLocalizedText(item: { name: string; nameAr?: string }, locale: 'en' | 'ar') {
  return locale === 'ar' && item.nameAr ? item.nameAr : item.name;
}

Image Handling

Products have multiple images. Always:

  • Display the first image as the main image
  • Show all images in a gallery
  • Handle missing images gracefully
function getProductImage(product: Product, index = 0) {
  return product.images[index] || '/placeholder-image.png';
}

Stock Display

Show stock status to users:

function getStockStatus(product: Product) {
  if (!product.stock) return 'In Stock';
  if (product.stock === 0) return 'Out of Stock';
  if (product.stock < 5) return `Only ${product.stock} left`;
  return 'In Stock';
}

Sale Display

Show sale price when available:

function renderPrice(product: Product) {
  if (product.compareAtPrice && product.compareAtPrice > product.basePrice) {
    return (
      <div>
        <span className="sale-price">{formatPrice(product.basePrice)}</span>
        <span className="original-price">{formatPrice(product.compareAtPrice)}</span>
      </div>
    );
  }
  return <span>{formatPrice(product.basePrice)}</span>;
}

💡 Examples

Complete Product Listing Component

import { MantarayStore } from '@mantaray-digital/store-sdk';

const store = new MantarayStore();

export async function ProductList() {
  const config = await store.store.getConfig();
  const { products } = await store.products.list();

  return (
    <div className="product-grid">
      {products.map(product => (
        <ProductCard key={product._id} product={product} config={config} />
      ))}
    </div>
  );
}

function ProductCard({ product, config }: { product: Product; config: StoreConfig }) {
  const price = formatPrice(product.basePrice, config.settings);
  const image = product.images[0] || '/placeholder.png';

  return (
    <div className="product-card">
      <img src={image} alt={product.name} />
      <h3>{product.name}</h3>
      <p className="price">{price}</p>
      {product.compareAtPrice && (
        <p className="original-price">
          {formatPrice(product.compareAtPrice, config.settings)}
        </p>
      )}
      <button onClick={() => addToCart(product._id)}>
        Add to Cart
      </button>
    </div>
  );
}

Complete Checkout Flow

import { MantarayStore } from '@mantaray-digital/store-sdk';

const store = new MantarayStore();

export async function CheckoutPage() {
  const [cart, shippingTiers] = await Promise.all([
    store.cart.get(),
    store.checkout.getShippingTiers()
  ]);

  const [selectedShipping, setSelectedShipping] = useState<string | null>(null);

  const handlePlaceOrder = async (address: ShippingAddress) => {
    if (!selectedShipping) {
      alert('Please select a shipping method');
      return;
    }

    const order = await store.checkout.createOrder({
      customerId: 'customer_id_here',
      shippingAddress: address,
      shippingTierId: selectedShipping
    });

    // Redirect to order confirmation
    window.location.href = `/order/${order.orderId}`;
  };

  return (
    <div className="checkout">
      <h1>Checkout</h1>

      {/* Cart Summary */}
      <section className="cart-summary">
        <h2>Order Summary</h2>
        {cart.items.map(item => (
          <div key={item.productId} className="cart-item">
            <img src={item.imageUrl} alt={item.name} />
            <div>
              <h3>{item.name}</h3>
              <p>Quantity: {item.quantity}</p>
              <p>Price: {formatPrice(item.price)}</p>
            </div>
          </div>
        ))}
        <div className="totals">
          <p>Subtotal: {formatPrice(cart.subtotal)}</p>
        </div>
      </section>

      {/* Shipping Options */}
      <section className="shipping-options">
        <h2>Shipping Method</h2>
        {shippingTiers.map(tier => (
          <label key={tier._id}>
            <input
              type="radio"
              name="shipping"
              value={tier._id}
              onChange={() => setSelectedShipping(tier._id)}
            />
            <div>
              <h3>{tier.name}</h3>
              <p>{tier.description}</p>
              <p>Cost: {tier.cost === 0 ? 'Free' : formatPrice(tier.cost)}</p>
              {tier.estimatedDays && (
                <p>Delivery: {tier.estimatedDays}</p>
              )}
            </div>
          </label>
        ))}
      </section>

      {/* Address Form */}
      <section className="address-form">
        <h2>Shipping Address</h2>
        <AddressForm onSubmit={handlePlaceOrder} />
      </section>
    </div>
  );
}

❌ Error Handling

The SDK provides detailed error information:

import { MantarayError, MantarayErrorCode } from '@mantaray-digital/store-sdk';

try {
  const product = await store.products.get('invalid_id');
} catch (error) {
  if (error instanceof MantarayError) {
    switch (error.code) {
      case MantarayErrorCode.API_KEY_REQUIRED:
        console.error('API key is missing');
        break;
      case MantarayErrorCode.NOT_FOUND:
        console.error('Product not found');
        break;
      case MantarayErrorCode.VALIDATION_ERROR:
        console.error('Invalid input:', error.message);
        break;
      case MantarayErrorCode.NETWORK_ERROR:
        console.error('Network error, please try again');
        break;
      default:
        console.error('An error occurred:', error.message);
    }
  }
}

📘 TypeScript Support

The SDK is fully typed. Import types as needed:

import type {
  Product,
  Cart,
  Order,
  CustomerProfile,
  ShippingAddress,
  MantarayStoreConfig
} from '@mantaray-digital/store-sdk';

📞 Support


📄 License

MIT © Mantaray Digital