@ecomiq/storefront 0.1.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # @ecomiq/storefront
2
2
 
3
- SDK **headless** para construir tu tienda con **Next.js** y **React** sobre la API de Ecomiq.
3
+ SDK **headless** para construir tu tienda con **Next.js** o **TanStack Start** y **React** sobre la API de Ecomiq.
4
4
 
5
- Incluye tipos, schemas Zod, cliente BFF (`/api/store`), hooks, `EcomiqProvider` y utilidades server. La interfaz de la tienda la implementas en tu proyecto.
5
+ Incluye tipos, schemas Zod, cliente BFF (`/api/store`), hooks, `EcomiqProvider` y bootstrap server por framework. **No incluye UI de tienda** (CMS, temas, checkout, etc.): la implementas en tu proyecto.
6
6
 
7
7
  ## Requisitos
8
8
 
9
9
  - Node.js 20+
10
- - Next.js 15+ (App Router)
11
10
  - React 19+
12
- - Tailwind CSS 4+ (opcional)
11
+ - **Next.js 15+** (App Router) *o* **TanStack Start** (`@tanstack/react-start`)
12
+ - Tailwind CSS 4+ (opcional; ver `/nextjs/css`)
13
13
 
14
14
  ## Instalación
15
15
 
@@ -19,6 +19,18 @@ npm install @ecomiq/storefront
19
19
  pnpm add @ecomiq/storefront
20
20
  ```
21
21
 
22
+ Instala también el peer de tu stack:
23
+
24
+ ```bash
25
+ # Next.js
26
+ pnpm add next react react-dom @tanstack/react-query better-auth
27
+
28
+ # TanStack Start
29
+ pnpm add @tanstack/react-start react react-dom @tanstack/react-query better-auth
30
+ ```
31
+
32
+ **Next.js** — en `next.config`:
33
+
22
34
  ```js
23
35
  /** @type {import('next').NextConfig} */
24
36
  const nextConfig = {
@@ -28,9 +40,9 @@ const nextConfig = {
28
40
  export default nextConfig;
29
41
  ```
30
42
 
31
- ## `ecomiq.config.js` (opcional)
43
+ ## `ecomiq.config.js`
32
44
 
33
- Puedes añadir en la **raíz de tu app** (junto a `next.config.js`) un archivo `ecomiq.config.js`, `ecomiq.config.cjs` o `ecomiq.config.mjs`:
45
+ En la **raíz de tu app** (`ecomiq.config.js`, `.cjs` o `.mjs`):
34
46
 
35
47
  ```js
36
48
  // @ts-check
@@ -43,20 +55,62 @@ const ecomiqConfig = {
43
55
  module.exports = ecomiqConfig;
44
56
  ```
45
57
 
46
- Si defines `slugStore`, `getPublicStore` usa ese slug como identificador de tienda y no lo infiere del header `Host`. Si no usas el archivo, la tienda se resuelve por el `Host` de la request (por ejemplo subdominio o dominio propio).
58
+ Si defines `slugStore`, `getPublicStore` usa ese slug (útil en localhost). Si no, deriva del header `Host`. Plantilla: [ecomiq.config.example.cjs](./ecomiq.config.example.cjs).
59
+
60
+ ## Puntos de entrada (npm)
61
+
62
+ | Import | Uso |
63
+ |--------|-----|
64
+ | `@ecomiq/storefront` | Solo config: `EcomiqConfig`, `loadEcomiqConfig`, `resolvePublicStoreKey` |
65
+ | `@ecomiq/storefront/nextjs` | Adapter Next + capa React (hooks, provider, BFF, auth, tipos) |
66
+ | `@ecomiq/storefront/nextjs/api` | Proxy `/api/store/*` (App Router) |
67
+ | `@ecomiq/storefront/nextjs/css` | Tailwind mínimo (opcional) |
68
+ | `@ecomiq/storefront/nextjs/postcss` | PostCSS de referencia |
69
+ | `@ecomiq/storefront/tanstack` | Adapter TanStack + **misma** capa React que `/nextjs` |
70
+ | `@ecomiq/storefront/tanstack/api` | Proxy `/api/store/*` (server routes) |
71
+
72
+ Importa **todo desde el adapter de tu framework** (`/nextjs` o `/tanstack`). En npm no hay subpaths públicos `/core` ni `/react`.
73
+
74
+ Los hooks (`useStoreProducts`, `useStoreCart`, `useStoreCollections`, etc.) tienen **una sola implementación** reexportada por ambos adapters; solo cambia el bootstrap server (`getPublicStore`, handler del proxy).
75
+
76
+ ### Peers
47
77
 
48
- Cuándo usarlo lo decides tú: en local, staging o producción. Plantilla: [ecomiq.config.example.cjs](./ecomiq.config.example.cjs).
78
+ | Paquete | Cuándo |
79
+ |---------|--------|
80
+ | `react`, `react-dom` | Siempre |
81
+ | `next` | Solo si usas `/nextjs` |
82
+ | `@tanstack/react-start` | Solo si usas `/tanstack` |
83
+ | `@opennextjs/cloudflare` | Opcional (settings vía KV) |
49
84
 
50
85
  ## Arquitectura
51
86
 
52
87
  ```
53
- Navegador → /api/store/* (proxy en tu Next.js) → API Ecomiq
54
- ↑ X-Store-Id (tras getPublicStore)
88
+ ┌─────────────────────────────┐
89
+ │ @ecomiq/storefront │ ecomiq.config.js
90
+ └──────────────┬──────────────┘
91
+ │
92
+ ┌─────────────────────────┴─────────────────────────┐
93
+ ▼ ▼
94
+ /nextjs (+ /nextjs/api) /tanstack (+ /tanstack/api)
95
+ getPublicStore · toNextJsStoreApiHandler getPublicStore · createTanStackStoreApiHandler
96
+ │ │
97
+ └─────────────────────────┬─────────────────────────┘
98
+ ▼
99
+ Capa React compartida (hooks, EcomiqProvider, cliente BFF, auth)
100
+ │
101
+ ▼
102
+ core/server — fetchPublicStoreSettings · handleStoreProxyRequest
103
+ │
104
+ ▼
105
+ Navegador → /api/store/* (proxy en tu app) → API Ecomiq
106
+ ↑ contexto X-Store-Id (tras getPublicStore)
55
107
  ```
56
108
 
57
- El browser no llama al upstream directo; montas el proxy con `toNextJsStoreApiHandler`.
109
+ El browser **no** llama al upstream directo; montas el proxy con el adapter de tu framework.
58
110
 
59
- ## Inicio rápido
111
+ ---
112
+
113
+ ## Inicio rápido — Next.js
60
114
 
61
115
  ### 1. Proxy BFF
62
116
 
@@ -88,7 +142,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo
88
142
  }
89
143
  ```
90
144
 
91
- `EcomiqProvider` hidrata el estado de la tienda (`useStore`, `setClientStoreId`) y React Query. La autenticación es opcional (pasos 5 y 6).
145
+ `EcomiqProvider` hidrata `useStore`, configura React Query y el `storeId` del cliente BFF.
92
146
 
93
147
  ### 3. Catálogo con hooks
94
148
 
@@ -123,23 +177,76 @@ export default async function CatalogPage() {
123
177
  }
124
178
  ```
125
179
 
126
- ### 5. Autenticación (Better Auth + SDK)
180
+ ---
181
+
182
+ ## Inicio rápido — TanStack Start
183
+
184
+ ### 1. Proxy BFF
185
+
186
+ Ruta splat: `/api/store/*` → `routes/api/store/$.ts`
187
+
188
+ ```ts
189
+ // src/routes/api/store/$.ts
190
+ import { createFileRoute } from "@tanstack/react-router";
191
+ import { createTanStackStoreApiHandler } from "@ecomiq/storefront/tanstack/api";
192
+
193
+ const storeApi = createTanStackStoreApiHandler({
194
+ apiBaseUrl: process.env.STOREFRONT_API_URL,
195
+ });
196
+
197
+ export const Route = createFileRoute("/api/store/$")({
198
+ server: { handlers: storeApi },
199
+ });
200
+ ```
201
+
202
+ ### 2. Tienda en root route
203
+
204
+ ```tsx
205
+ // src/routes/__root.tsx
206
+ import { createRootRoute, Outlet } from "@tanstack/react-router";
207
+ import { getPublicStore, EcomiqProvider } from "@ecomiq/storefront/tanstack";
208
+
209
+ export const Route = createRootRoute({
210
+ loader: async () => getPublicStore(),
211
+ component: RootComponent,
212
+ });
213
+
214
+ function RootComponent() {
215
+ const { data: store } = Route.useLoaderData();
216
+ return (
217
+ <EcomiqProvider store={store}>
218
+ <Outlet />
219
+ </EcomiqProvider>
220
+ );
221
+ }
222
+ ```
223
+
224
+ ### 3. Hooks (idénticos a Next)
225
+
226
+ ```tsx
227
+ import { useStoreProducts, useStoreCart } from "@ecomiq/storefront/tanstack";
228
+ ```
229
+
230
+ ---
231
+
232
+ ## Autenticación (Better Auth + SDK)
127
233
 
128
- Instala [Better Auth](https://www.better-auth.com/) y los plugins del SDK (OAuth password grant con credenciales Ecomiq).
234
+ Instala [Better Auth](https://www.better-auth.com/).
129
235
 
130
236
  **Servidor:**
131
237
 
132
238
  ```ts
133
239
  // lib/auth.ts
134
240
  import { betterAuth } from "better-auth";
135
- import { ecomiqOAuthPassword } from "@ecomiq/storefront/react";
241
+ import { ecomiqOAuthPassword } from "@ecomiq/storefront/nextjs";
242
+ // TanStack: import { ecomiqOAuthPassword } from "@ecomiq/storefront/tanstack";
136
243
 
137
244
  export const auth = betterAuth({
138
245
  plugins: [ecomiqOAuthPassword()],
139
246
  });
140
247
  ```
141
248
 
142
- **Ruta API:**
249
+ **Ruta API (Next.js):**
143
250
 
144
251
  ```ts
145
252
  // app/api/auth/[...all]/route.ts
@@ -156,26 +263,22 @@ export const { GET, POST } = toNextJsHandler(auth);
156
263
  "use client";
157
264
 
158
265
  import { createAuthClient } from "better-auth/react";
159
- import { ecomiqOAuthPasswordClient } from "@ecomiq/storefront/react";
266
+ import { ecomiqOAuthPasswordClient } from "@ecomiq/storefront/nextjs";
160
267
 
161
268
  export const authClient = createAuthClient({
162
269
  plugins: [ecomiqOAuthPasswordClient()],
163
270
  });
164
271
  ```
165
272
 
166
- **Layout** — añade `authClient` al provider del paso 2:
273
+ **Layout** — pasa `authClient` al provider:
167
274
 
168
275
  ```tsx
169
- import { authClient } from "@/lib/auth-client";
170
-
171
276
  <EcomiqProvider store={store} authClient={authClient}>
172
277
  {children}
173
278
  </EcomiqProvider>
174
279
  ```
175
280
 
176
- ### 6. Login, registro y logout
177
-
178
- Con el paso 5 configurado:
281
+ ### Login, registro y logout
179
282
 
180
283
  ```tsx
181
284
  "use client";
@@ -184,8 +287,6 @@ import {
184
287
  useStore,
185
288
  useStorefrontAuthClient,
186
289
  useStorefrontSession,
187
- } from "@ecomiq/storefront/react";
188
- import {
189
290
  registerCustomer,
190
291
  signInWithEcomiqCredentials,
191
292
  } from "@ecomiq/storefront/nextjs";
@@ -216,54 +317,44 @@ export function AccountActions() {
216
317
  return (
217
318
  <div>
218
319
  <p>Hola, {user?.firstName ?? user?.email}</p>
219
- <button type="button" onClick={() => signOut()}>
220
- Cerrar sesión
221
- </button>
320
+ <button type="button" onClick={() => signOut()}>Cerrar sesión</button>
222
321
  </div>
223
322
  );
224
323
  }
225
324
 
226
- return (
227
- <div>
228
- {/* Tu UI: formularios que llaman handleLogin / handleRegister */}
229
- </div>
230
- );
325
+ return <div>{/* Tu UI */}</div>;
231
326
  }
232
327
  ```
233
328
 
234
- - **Login:** `signInWithEcomiqCredentials` (headers de tienda los resuelve el SDK).
235
- - **Registro:** `registerCustomer`, luego login con las mismas credenciales.
236
- - **Logout:** `signOut()` de `useStorefrontSession`.
329
+ En TanStack, cambia el import a `@ecomiq/storefront/tanstack`.
237
330
 
238
- ## Puntos de entrada (npm)
331
+ ---
239
332
 
240
- | Import | Uso |
241
- |--------|-----|
242
- | `@ecomiq/storefront` | `EcomiqConfig`, `loadEcomiqConfig` (tipado de `ecomiq.config.js`) |
243
- | `@ecomiq/storefront/core` | Tipos, Zod, utilidades |
244
- | `@ecomiq/storefront/react` | `EcomiqProvider`, hooks, sesión (`useStorefrontSession`), plugins Better Auth |
245
- | `@ecomiq/storefront/nextjs` | `getPublicStore`, cliente BFF, `registerCustomer`, `signInWithEcomiqCredentials`, hooks `useStore*` |
246
- | `@ecomiq/storefront/nextjs/api` | Proxy `/api/store/*` |
333
+ ## Qué incluye cada adapter
334
+
335
+ Desde `/nextjs` o `/tanstack` obtienes:
247
336
 
248
- El cliente BFF se importa desde **`/nextjs`** (no hay subpath `./client` en npm).
337
+ - **Server:** `getPublicStore`, `getHostname`, `fetchPublicStoreSettings` (agnóstico)
338
+ - **Provider:** `EcomiqProvider`, `useStore`, `usePublicStore`
339
+ - **Hooks BFF:** `useStoreProducts`, `useStoreCollections`, `useStorePages`, `useStoreCart`
340
+ - **Cliente:** `getStorefrontProducts`, `registerCustomer`, servicios de carrito/cuenta, etc.
341
+ - **Auth:** `ecomiqOAuthPassword`, `ecomiqOAuthPasswordClient`, `signInWithEcomiqCredentials`, `useStorefrontSession`
342
+ - **Tipos y Zod:** `StorefrontSettings`, `StorefrontProduct`, schemas, `formatCurrency`
343
+ - **Utilidades:** `findStorefrontCmsPageBySlug`, selector de variantes de producto
249
344
 
250
345
  ## Variables de entorno
251
346
 
252
347
  | Variable | Descripción |
253
348
  |----------|-------------|
254
349
  | `STOREFRONT_API_URL` | URL base de la API de tienda (proxy) |
255
- | `PUBLIC_STORE_SETTINGS_SOURCE` | `api` (default) o `kv` (despliegues con KV) |
256
-
257
- Tras `getPublicStore`, el SDK configura el contexto de tienda en el servidor para las llamadas al proxy.
350
+ | `PUBLIC_STORE_SETTINGS_SOURCE` | `api` (default) o `kv` (Cloudflare KV vía `@opennextjs/cloudflare`) |
258
351
 
259
352
  **Identificador de tienda:** `slugStore` en `ecomiq.config.*` si existe; si no, header `Host`.
260
353
 
261
- ## Publicación del paquete
262
-
263
- Mantenimiento del tarball npm: [PUBLISHING.md](./PUBLISHING.md).
264
-
265
354
  ## Dependencias recomendadas
266
355
 
356
+ **Next.js:**
357
+
267
358
  ```json
268
359
  {
269
360
  "dependencies": {
@@ -277,6 +368,21 @@ Mantenimiento del tarball npm: [PUBLISHING.md](./PUBLISHING.md).
277
368
  }
278
369
  ```
279
370
 
371
+ **TanStack Start:**
372
+
373
+ ```json
374
+ {
375
+ "dependencies": {
376
+ "@ecomiq/storefront": "^0.1.0",
377
+ "@tanstack/react-query": "^5.66.0",
378
+ "@tanstack/react-start": "^1.0.0",
379
+ "better-auth": "^1.6.0",
380
+ "react": "^19.0.0",
381
+ "react-dom": "^19.0.0"
382
+ }
383
+ }
384
+ ```
385
+
280
386
  ## Soporte
281
387
 
282
388
  Documentación y credenciales: [ecomiq.pe](https://ecomiq.pe) (o el portal de tu organización).