@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 +159 -53
- package/dist/index-C-xqLr1g.d.ts +6551 -0
- package/dist/nextjs/api.d.ts +10 -14
- package/dist/nextjs/api.js +74 -41
- package/dist/nextjs/api.js.map +1 -1
- package/dist/nextjs.d.ts +21 -3193
- package/dist/nextjs.js +1759 -588
- package/dist/nextjs.js.map +1 -1
- package/dist/store-proxy-handler-CNzKlTFA.d.ts +10 -0
- package/dist/tanstack/api.d.ts +41 -0
- package/dist/tanstack/api.js +196 -0
- package/dist/tanstack/api.js.map +1 -0
- package/dist/tanstack.d.ts +30 -0
- package/dist/{react.js → tanstack.js} +2466 -2172
- package/dist/tanstack.js.map +1 -0
- package/package.json +21 -13
- package/dist/collection-anMkdWa7.d.ts +0 -4333
- package/dist/core.d.ts +0 -3396
- package/dist/core.js +0 -1239
- package/dist/core.js.map +0 -1
- package/dist/find-storefront-cms-page-DNwLo1iV.d.ts +0 -178
- package/dist/react.d.ts +0 -964
- package/dist/react.js.map +0 -1
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
|
|
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
|
-
-
|
|
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`
|
|
43
|
+
## `ecomiq.config.js`
|
|
32
44
|
|
|
33
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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
|
|
109
|
+
El browser **no** llama al upstream directo; montas el proxy con el adapter de tu framework.
|
|
58
110
|
|
|
59
|
-
|
|
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
|
|
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
|
-
|
|
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/)
|
|
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/
|
|
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/
|
|
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** —
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
331
|
+
---
|
|
239
332
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
|
|
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` (
|
|
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).
|