Skip to content

8 headless packages,
each with clear responsibility

Import only what you need. Swap an adapter without rewriting the rest. Each package is a class or set of pure functions with injected dependencies.

Technical documentation

  1. @kmee/shop-types

    Foundation

    Shared types (Product, ProductDetail, Cart, Brand, Category) and pure helpers. AuthProvider and Logger contracts. Imported by every other package.

    Main exports

    • Product
    • ProductDetail
    • Cart
    • AuthProvider
    • Logger
    • formatPrice
    • applyFilters
    @kmee/shop-types
    import { formatPrice, applyFilters } from "@kmee/shop-types"
    
    formatPrice({ value: 49.9 }, { locale: "pt-BR", currency: "BRL" })
    // -> "R$ 49,90"
    
    applyFilters(products, { categories: ["Films"] }, { min: 10, max: 100 })
  2. @kmee/shopinvader-client

    Backend client

    Headless client for Odoo via Shopinvader. CartService, SalesService, AddressService, DeliveryCarriersService, LeadsService on top of OdooHttpClient with retry, timeout and injectable auth.

    Main exports

    • OdooHttpClient
    • CartService
    • SalesService
    • AddressService
    • DeliveryCarriersService
    • LeadsService
    @kmee/shopinvader-client
    import { OdooHttpClient, SalesService } from "@kmee/shopinvader-client"
    
    const sales = new SalesService(
      new OdooHttpClient({
        baseUrl,
        auth,
        getExtraHeaders: async () => ({ apikey: SUPABASE_ANON_KEY }),
      }),
    )
    
    const orders = await sales.list({ limit: 10 })
  3. @kmee/elasticsearch-shop

    Search & catalog

    Elasticsearch client for product listings, search and detail, categories and brands. ProductService collapses by model.name, supports tag filters, slug fallback by segment wildcards, and Levenshtein for picking the best hit.

    Main exports

    • ProductService
    • searchCategories
    • searchBrands
    @kmee/elasticsearch-shop
    import { ProductService } from "@kmee/elasticsearch-shop"
    
    const products = new ProductService(esConfig)
    
    await products.search({ searchTerm: "vinyl", page: 1, pageSize: 12 })
    await products.getByURLKey("oratape-mt80p")
    await products.getRelatedProducts([{ name: "ThermoFlex Plus" }])
  4. @kmee/supabase-auth-shop

    Auth adapter

    Adapter implementing AuthProvider on top of @supabase/supabase-js. Plug it into an OdooHttpClient and the Supabase token flows through to Odoo automatically.

    Main exports

    • createSupabaseAuthProvider
    @kmee/supabase-auth-shop
    import { createSupabaseAuthProvider } from "@kmee/supabase-auth-shop"
    
    const auth = createSupabaseAuthProvider(supabase)
    // auth implements AuthProvider - plug into OdooHttpClient.
    
    await auth.getToken()         // -> access_token or null
    await auth.isAuthenticated()  // -> false for anonymous by default
  5. @kmee/shop-storage

    Local persistence

    Local persistence: CartStorage (UUID + cart data with expiry) and CartItemsMetadataStorage (min_order_qty per item). Configurable storage keys.

    Main exports

    • CartStorage
    • CartItemsMetadataStorage
    @kmee/shop-storage
    import { CartStorage } from "@kmee/shop-storage"
    
    const cartStorage = new CartStorage({
      cartKey: "myshop_cart",
      uuidKey: "myshop_cart_uuid",
      expiryDays: 60,
    })
    
    cartStorage.getOrCreateCartUuid()
    cartStorage.saveCartData({ uuid, items: [], lastUpdated: 0 })
  6. @kmee/admin-session

    Admin auth

    Admin session: HttpOnly cookie JWT (jose), Next.js cookie helpers, runtime-agnostic getClientIp, and AdminRateLimiter with pluggable store (MemoryRateLimitStore by default; swap for Redis/KV in production).

    Main exports

    • signAdminSessionJwt
    • verifyAdminSessionToken
    • AdminRateLimiter
    • getClientIp
    @kmee/admin-session
    import {
      signAdminSessionJwt,
      AdminRateLimiter,
      getClientIp,
    } from "@kmee/admin-session"
    
    const limiter = new AdminRateLimiter()
    const ip = getClientIp(request.headers)
    const decision = await limiter.assertAllowed(ip)
    if (!decision.ok) return new Response("rate limited", { status: 429 })
    
    const token = await signAdminSessionJwt({ secret: SECRET })
  7. @kmee/shop-react

    React layer

    React providers and hooks to wire it all together: <CartProvider>, <WishlistProvider>, <SupabaseAuthProvider>, useProductsPage, useCategoriesPage, useBrandsAndCategories.

    Main exports

    • CartProvider
    • WishlistProvider
    • SupabaseAuthProvider
    • useCart
    • useWishlist
    • useAuth
    • useProductsPage
    @kmee/shop-react
    import { useCart, useWishlist, useAuth } from "@kmee/shop-react"
    
    const { cart, addToCart, removeItem, loading } = useCart()
    const { items, add, isInWishlist } = useWishlist()
    const { user, signInWithPassword, signOut } = useAuth()
  8. @kmee/shop-ui

    UI components

    Opinionated components (Tailwind + Radix): primitives (Button, Badge, Skeleton, Sheet, DropdownMenu, Select, Input, Tabs, Table) and ~20 domain components. Theme via CSS vars + Tailwind preset compatible with shadcn.

    Main exports

    • ProductCard
    • AddToCartButton
    • CartDrawer
    • ProductGallery
    • ProductFilters
    • HeroCarousel
    • GlobalHeader
    • Footer
    @kmee/shop-ui
    // client tailwind.config.ts
    import shopUiPreset from "@kmee/shop-ui/tailwind-preset"
    
    export default {
      presets: [shopUiPreset],
      content: [
        "./app/**/*.{ts,tsx}",
        "./node_modules/@kmee/shop-ui/src/**/*.{ts,tsx}",
      ],
    }

How to compose

You only consume what makes sense for your app.

// lib/clients.ts - typical composition in a Next.js app

import { createClient } from "@supabase/supabase-js"
import { createSupabaseAuthProvider } from "@kmee/supabase-auth-shop"
import { OdooHttpClient, CartService, SalesService } from "@kmee/shopinvader-client"
import { ProductService } from "@kmee/elasticsearch-shop"
import { CartStorage } from "@kmee/shop-storage"

export const supabase = createClient(URL, ANON_KEY)
const auth = createSupabaseAuthProvider(supabase)

const odoo = new OdooHttpClient({ baseUrl, auth })

export const cart      = new CartService(odoo)
export const sales     = new SalesService(odoo)
export const products  = new ProductService(esConfig)
export const cartStorage = new CartStorage()

Each class is a composition point. The OdooHttpClient receives the auth provider; CartService receives the client; CartProvider (React side) receives the service. Nothing tightly coupled.

Need a custom package?

Have a use case that doesn't fit the 8 libs? KMEE develops specific adapters (payment gateway, third-party ERP, marketplace).