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

@quadcore-lib/storefront-core

v0.1.1

Published

Hooks de lógica para construir un storefront (catálogo, carrito, checkout y seguimiento de orden) contra el backend de Quadcore. **Sin componentes visuales** — eso vive en `@quadcore-lib/storefront-ui` (roadmap 3.2). Este paquete es el equivalente, del la

Readme

@quadcore-lib/storefront-core

Hooks de lógica para construir un storefront (catálogo, carrito, checkout y seguimiento de orden) contra el backend de Quadcore. Sin componentes visuales — eso vive en @quadcore-lib/storefront-ui (roadmap 3.2). Este paquete es el equivalente, del lado del cliente final, a lo que admin-core + admin-* son para el panel de administración.

Instalación

npm install @quadcore-lib/storefront-core

Uso

import { useProducts, useCart, useCheckout } from "@quadcore-lib/storefront-core";

const API_URL = "https://api.mitienda.com";

function Catalog() {
  const { data, loading } = useProducts(API_URL, { page: 1, pageSize: 20 });
  const { addItem, items, subtotal } = useCart();

  if (loading) return <p>Cargando...</p>;
  return (
    <ul>
      {data?.items.map((p) => (
        <li key={p.id}>
          {p.name} — ${p.price}
          <button onClick={() => addItem({ productId: p.id, name: p.name, unitPrice: p.price, currency: p.currency })}>
            Agregar al carrito
          </button>
        </li>
      ))}
    </ul>
  );
}

function Checkout() {
  const { items, clear } = useCart();
  const { checkout, submitting, error, order } = useCheckout(API_URL);

  async function handleSubmit() {
    const created = await checkout(
      items.map((i) => ({ productId: i.productId, quantity: i.quantity })),
      { customerEmail: "[email protected]" },
    );
    clear();
  }

  return (
    <button disabled={submitting} onClick={handleSubmit}>
      {submitting ? "Procesando..." : "Confirmar compra"}
    </button>
  );
}

API

| Export | Tipo | Descripción | |---|---|---| | useProducts(apiUrl, params?) | hook | Lista paginada de productos activos (GET /api/products, fuerza isActive=true) | | useProduct(apiUrl, id) | hook | Detalle de un producto (GET /api/products/:id) | | useCart() | hook | Carrito persistido en localStorage, 100% cliente | | useCheckout(apiUrl) | hook | Crea la orden (POST /api/orders) | | useOrderStatus(apiUrl, orderId, options?) | hook | Polling del estado de una orden — ver gap conocido abajo | | listProducts, getProduct | función | Cliente API de productos, usados internamente por los hooks | | createOrder, getOrder | función | Cliente API de órdenes, usados internamente por los hooks | | newIdempotencyKey() | función | Genera una clave de idempotencia (uuid v4, con fallback sin Web Crypto) | | Product, Paginated<T>, ListProductsParams | tipo | — | | CartItem, CheckoutItemInput, CheckoutInput | tipo | — | | Order, OrderItem, OrderStatus, ORDER_STATUSES | tipo/const | — |

useCart()

Carrito 100% cliente: no pega contra el backend. unitPrice/currency en CartItem son solo un snapshot para mostrar — el precio real que se cobra siempre lo resuelve orders-server en el momento de crear la orden (precio autoritativo de servidor). Persiste en localStorage bajo la key qc-storefront-cart; si localStorage no está disponible (SSR, modo privado, cuota llena) el carrito sigue funcionando en memoria para esa sesión, solo que no persiste entre recargas.

useCheckout(apiUrl)

Llama a POST /api/orders. No vacía el carrito automáticamente — es responsabilidad del componente que llama a checkout(...) hacer cart.clear() una vez que la promesa resuelve con éxito.

Manda un header Idempotency-Key por intento de compra, y conserva la misma clave mientras el checkout falle: si el usuario reintenta tras un error de red, o hace doble click, el backend devuelve la orden original en vez de crear una segunda y descontar stock dos veces. Al confirmar con éxito la clave se descarta, así que el próximo checkout es una compra nueva.

No hay que hacer nada para que funcione. Si armás tu propio flujo con createOrder(...) directo, pasale vos la clave como cuarto argumento y respetá esa misma regla — newIdempotencyKey() está exportado para eso:

import { createOrder, newIdempotencyKey } from "@quadcore-lib/storefront-core";

const key = newIdempotencyKey();          // una por intento de compra
await createOrder(apiUrl, items, input, key);  // reusala si reintentás

Gap conocido: seguimiento de orden (useOrderStatus)

useOrderStatus hace polling sobre GET /api/orders/:id cada intervalMs (default 5000ms, configurable) hasta que el estado de la orden llega a uno de stopOn (default ['delivered', 'cancelled']). Usa setTimeout recursivo (no setInterval) para no superponer pedidos si una respuesta tarda más que el intervalo, y reintenta indefinidamente ante errores de red (no frena el polling por un error transitorio).

Hoy, GET /orders/:id en orders-server es un endpoint admin-only (@Roles('admin')) — no existe un endpoint público o de cliente para que un comprador no autenticado (o autenticado sin rol admin) consulte el estado de su propia orden. Este paquete no resuelve ese gap: expone el hook asumiendo que el proyecto consumidor agrega, del lado del backend, una forma de exponer ese lookup de forma segura para el dueño de la orden — por ejemplo:

  • Un endpoint público GET /orders/track/:orderNumber que exija además el customerEmail como query param y solo devuelva la orden si coincide.
  • Un token de seguimiento opaco emitido al crear la orden (devuelto en la respuesta de POST /orders) que el cliente guarda y usa en vez del id.
  • Autenticar al comprador (Auth0) y agregar un guard que permita GET /orders/:id cuando order.userId === req.user.id, además de a admin.

Hasta que exista alguna de esas variantes, useOrderStatus fallará con 401/403 contra un backend que no haya sido extendido. No se implementó ninguna de las opciones anteriores en este paquete porque las tres implican una decisión de producto (qué tan fácil de adivinar puede ser el orderNumber, si vale la pena forzar login para comprar, etc.) que le corresponde a cada proyecto, no a la librería.

Requisitos

  • react >= 18, react-dom >= 18 (peer).
  • @quadcore-lib/core ^0.1.5 (dependency — provee fetchWithConfig, useFetch, ApiResponse).
  • Un backend con products-server y orders-server montados (ver packages/backend/products-server y packages/backend/orders-server).