@ecomiq/storefront 0.1.1 → 0.1.2
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 +144 -47
- package/dist/..d.ts +30 -0
- package/dist/core.d.ts +47 -42
- package/dist/core.js +324 -212
- package/dist/core.js.map +1 -1
- package/dist/{find-storefront-cms-page-c8CktSz7.d.ts → find-storefront-cms-page-DNwLo1iV.d.ts} +17 -3
- package/dist/index.d.ts +30 -0
- package/dist/index.js +103 -0
- package/dist/index.js.map +1 -0
- package/dist/nextjs.d.ts +119 -111
- package/dist/nextjs.js +336 -43
- package/dist/nextjs.js.map +1 -1
- package/dist/react.d.ts +84 -83
- package/dist/react.js +342 -238
- package/dist/react.js.map +1 -1
- package/package.json +5 -1
- package/dist/{store-to_vuWRF.d.ts → collection-anMkdWa7.d.ts} +2666 -2666
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# @ecomiq/storefront
|
|
2
2
|
|
|
3
|
-
SDK **headless** para construir tu
|
|
3
|
+
SDK **headless** para construir tu tienda con **Next.js** y **React** sobre la API de Ecomiq.
|
|
4
4
|
|
|
5
|
-
Incluye
|
|
5
|
+
Incluye tipos, schemas Zod, cliente BFF (`/api/store`), hooks, `EcomiqProvider` y utilidades server. La interfaz de la tienda la implementas en tu proyecto.
|
|
6
6
|
|
|
7
7
|
## Requisitos
|
|
8
8
|
|
|
9
9
|
- Node.js 20+
|
|
10
10
|
- Next.js 15+ (App Router)
|
|
11
11
|
- React 19+
|
|
12
|
-
- Tailwind CSS 4+
|
|
12
|
+
- Tailwind CSS 4+ (opcional)
|
|
13
13
|
|
|
14
14
|
## Instalación
|
|
15
15
|
|
|
@@ -28,14 +28,33 @@ const nextConfig = {
|
|
|
28
28
|
export default nextConfig;
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
## `ecomiq.config.js` (opcional)
|
|
32
|
+
|
|
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`:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
// @ts-check
|
|
37
|
+
|
|
38
|
+
/** @type {import('@ecomiq/storefront').EcomiqConfig} */
|
|
39
|
+
const ecomiqConfig = {
|
|
40
|
+
slugStore: "mi-tienda",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
module.exports = ecomiqConfig;
|
|
44
|
+
```
|
|
45
|
+
|
|
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).
|
|
47
|
+
|
|
48
|
+
Cuándo usarlo lo decides tú: en local, staging o producción. Plantilla: [ecomiq.config.example.cjs](./ecomiq.config.example.cjs).
|
|
49
|
+
|
|
31
50
|
## Arquitectura
|
|
32
51
|
|
|
33
52
|
```
|
|
34
53
|
Navegador → /api/store/* (proxy en tu Next.js) → API Ecomiq
|
|
35
|
-
↑ X-Store-Id (
|
|
54
|
+
↑ X-Store-Id (tras getPublicStore)
|
|
36
55
|
```
|
|
37
56
|
|
|
38
|
-
El
|
|
57
|
+
El browser no llama al upstream directo; montas el proxy con `toNextJsStoreApiHandler`.
|
|
39
58
|
|
|
40
59
|
## Inicio rápido
|
|
41
60
|
|
|
@@ -50,12 +69,11 @@ export const { GET, POST, PUT, PATCH, DELETE } = toNextJsStoreApiHandler({
|
|
|
50
69
|
});
|
|
51
70
|
```
|
|
52
71
|
|
|
53
|
-
### 2.
|
|
72
|
+
### 2. Layout y tienda
|
|
54
73
|
|
|
55
74
|
```tsx
|
|
56
75
|
// app/layout.tsx (Server Component)
|
|
57
76
|
import { getPublicStore, EcomiqProvider } from "@ecomiq/storefront/nextjs";
|
|
58
|
-
import { authClient } from "./auth-client";
|
|
59
77
|
|
|
60
78
|
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
|
61
79
|
const { data: store, error } = await getPublicStore();
|
|
@@ -63,14 +81,14 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
|
|
63
81
|
// tu página de error / tienda no encontrada
|
|
64
82
|
}
|
|
65
83
|
return (
|
|
66
|
-
<EcomiqProvider store={store}
|
|
84
|
+
<EcomiqProvider store={store}>
|
|
67
85
|
{children}
|
|
68
86
|
</EcomiqProvider>
|
|
69
87
|
);
|
|
70
88
|
}
|
|
71
89
|
```
|
|
72
90
|
|
|
73
|
-
`EcomiqProvider`
|
|
91
|
+
`EcomiqProvider` hidrata el estado de la tienda (`useStore`, `setClientStoreId`) y React Query. La autenticación es opcional (pasos 5 y 6).
|
|
74
92
|
|
|
75
93
|
### 3. Catálogo con hooks
|
|
76
94
|
|
|
@@ -105,67 +123,146 @@ export default async function CatalogPage() {
|
|
|
105
123
|
}
|
|
106
124
|
```
|
|
107
125
|
|
|
108
|
-
### 5.
|
|
126
|
+
### 5. Autenticación (Better Auth + SDK)
|
|
109
127
|
|
|
110
|
-
|
|
111
|
-
@import "tailwindcss";
|
|
112
|
-
@import "@ecomiq/storefront/nextjs/css";
|
|
113
|
-
```
|
|
128
|
+
Instala [Better Auth](https://www.better-auth.com/) y los plugins del SDK (OAuth password grant con credenciales Ecomiq).
|
|
114
129
|
|
|
115
|
-
|
|
130
|
+
**Servidor:**
|
|
116
131
|
|
|
117
|
-
|
|
132
|
+
```ts
|
|
133
|
+
// lib/auth.ts
|
|
134
|
+
import { betterAuth } from "better-auth";
|
|
135
|
+
import { ecomiqOAuthPassword } from "@ecomiq/storefront/react";
|
|
118
136
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
| `@ecomiq/storefront/nextjs` | `getPublicStore`, `EcomiqProvider`, cliente BFF, hooks `useStore*` |
|
|
124
|
-
| `@ecomiq/storefront/nextjs/api` | Handler del proxy `/api/store/*` |
|
|
125
|
-
| `@ecomiq/storefront/nextjs/css` | Tailwind base opcional |
|
|
126
|
-
| `@ecomiq/storefront/nextjs/postcss` | PostCSS de referencia |
|
|
137
|
+
export const auth = betterAuth({
|
|
138
|
+
plugins: [ecomiqOAuthPassword()],
|
|
139
|
+
});
|
|
140
|
+
```
|
|
127
141
|
|
|
128
|
-
|
|
142
|
+
**Ruta API:**
|
|
129
143
|
|
|
130
|
-
|
|
144
|
+
```ts
|
|
145
|
+
// app/api/auth/[...all]/route.ts
|
|
146
|
+
import { toNextJsHandler } from "better-auth/next-js";
|
|
147
|
+
import { auth } from "@/lib/auth";
|
|
131
148
|
|
|
132
|
-
|
|
149
|
+
export const { GET, POST } = toNextJsHandler(auth);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Cliente:**
|
|
133
153
|
|
|
134
154
|
```ts
|
|
155
|
+
// lib/auth-client.ts
|
|
156
|
+
"use client";
|
|
157
|
+
|
|
158
|
+
import { createAuthClient } from "better-auth/react";
|
|
159
|
+
import { ecomiqOAuthPasswordClient } from "@ecomiq/storefront/react";
|
|
160
|
+
|
|
161
|
+
export const authClient = createAuthClient({
|
|
162
|
+
plugins: [ecomiqOAuthPasswordClient()],
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Layout** — añade `authClient` al provider del paso 2:
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import { authClient } from "@/lib/auth-client";
|
|
170
|
+
|
|
171
|
+
<EcomiqProvider store={store} authClient={authClient}>
|
|
172
|
+
{children}
|
|
173
|
+
</EcomiqProvider>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 6. Login, registro y logout
|
|
177
|
+
|
|
178
|
+
Con el paso 5 configurado:
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
"use client";
|
|
182
|
+
|
|
135
183
|
import {
|
|
136
|
-
|
|
137
|
-
|
|
184
|
+
useStore,
|
|
185
|
+
useStorefrontAuthClient,
|
|
186
|
+
useStorefrontSession,
|
|
138
187
|
} from "@ecomiq/storefront/react";
|
|
188
|
+
import {
|
|
189
|
+
registerCustomer,
|
|
190
|
+
signInWithEcomiqCredentials,
|
|
191
|
+
} from "@ecomiq/storefront/nextjs";
|
|
192
|
+
|
|
193
|
+
export function AccountActions() {
|
|
194
|
+
const { data: store } = useStore();
|
|
195
|
+
const authClient = useStorefrontAuthClient();
|
|
196
|
+
const { isAuthenticated, user, signOut, refetch } = useStorefrontSession();
|
|
197
|
+
|
|
198
|
+
async function handleLogin(email: string, password: string) {
|
|
199
|
+
await signInWithEcomiqCredentials(authClient, store, { email, password });
|
|
200
|
+
await refetch();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function handleRegister(input: {
|
|
204
|
+
email: string;
|
|
205
|
+
password: string;
|
|
206
|
+
firstName: string;
|
|
207
|
+
lastName: string;
|
|
208
|
+
countryIsoCode: string;
|
|
209
|
+
phone: string;
|
|
210
|
+
}) {
|
|
211
|
+
await registerCustomer(input);
|
|
212
|
+
await handleLogin(input.email, input.password);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (isAuthenticated) {
|
|
216
|
+
return (
|
|
217
|
+
<div>
|
|
218
|
+
<p>Hola, {user?.firstName ?? user?.email}</p>
|
|
219
|
+
<button type="button" onClick={() => signOut()}>
|
|
220
|
+
Cerrar sesión
|
|
221
|
+
</button>
|
|
222
|
+
</div>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<div>
|
|
228
|
+
{/* Tu UI: formularios que llaman handleLogin / handleRegister */}
|
|
229
|
+
</div>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
139
232
|
```
|
|
140
233
|
|
|
141
|
-
|
|
234
|
+
- **Login:** `signInWithEcomiqCredentials` (headers de tienda los resuelve el SDK).
|
|
235
|
+
- **Registro:** `registerCustomer`, luego login con las mismas credenciales.
|
|
236
|
+
- **Logout:** `signOut()` de `useStorefrontSession`.
|
|
142
237
|
|
|
143
|
-
|
|
144
|
-
|----------|-------------|
|
|
145
|
-
| `STOREFRONT_API_URL` | URL base de la API de tienda (proxy) |
|
|
146
|
-
| `PUBLIC_STORE_SETTINGS_SOURCE` | `api` (default) o `kv` (OpenNext / Cloudflare) |
|
|
238
|
+
## Puntos de entrada (npm)
|
|
147
239
|
|
|
148
|
-
|
|
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/*` |
|
|
149
247
|
|
|
150
|
-
|
|
248
|
+
El cliente BFF se importa desde **`/nextjs`** (no hay subpath `./client` en npm).
|
|
151
249
|
|
|
152
|
-
|
|
250
|
+
## Variables de entorno
|
|
153
251
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
- Dependencia de `@ecomiq/ui`
|
|
252
|
+
| Variable | Descripción |
|
|
253
|
+
|----------|-------------|
|
|
254
|
+
| `STOREFRONT_API_URL` | URL base de la API de tienda (proxy) |
|
|
255
|
+
| `PUBLIC_STORE_SETTINGS_SOURCE` | `api` (default) o `kv` (despliegues con KV) |
|
|
159
256
|
|
|
160
|
-
|
|
257
|
+
Tras `getPublicStore`, el SDK configura el contexto de tienda en el servidor para las llamadas al proxy.
|
|
161
258
|
|
|
162
|
-
|
|
259
|
+
**Identificador de tienda:** `slugStore` en `ecomiq.config.*` si existe; si no, header `Host`.
|
|
163
260
|
|
|
164
|
-
|
|
261
|
+
## Publicación del paquete
|
|
165
262
|
|
|
166
|
-
|
|
263
|
+
Mantenimiento del tarball npm: [PUBLISHING.md](./PUBLISHING.md).
|
|
167
264
|
|
|
168
|
-
## Dependencias recomendadas
|
|
265
|
+
## Dependencias recomendadas
|
|
169
266
|
|
|
170
267
|
```json
|
|
171
268
|
{
|
package/dist/..d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
declare const EcomiqConfigSchema: z.ZodObject<{
|
|
4
|
+
/**
|
|
5
|
+
* Slug de tienda en Ecomiq (p. ej. `mi-tienda` para `mi-tienda.ecomiq.app`).
|
|
6
|
+
* Si está definido, tiene prioridad sobre el `Host` de la request (útil en `localhost`).
|
|
7
|
+
*/
|
|
8
|
+
slugStore: z.ZodOptional<z.ZodString>;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
slugStore?: string | undefined;
|
|
11
|
+
}, {
|
|
12
|
+
slugStore?: string | undefined;
|
|
13
|
+
}>;
|
|
14
|
+
type EcomiqConfig = z.infer<typeof EcomiqConfigSchema>;
|
|
15
|
+
/**
|
|
16
|
+
* Carga `ecomiq.config.js` (o `.cjs` / `.mjs`) desde la raíz del proyecto (`process.cwd()`).
|
|
17
|
+
* Resultado en caché por proceso.
|
|
18
|
+
*/
|
|
19
|
+
declare function loadEcomiqConfig(cwd?: string): EcomiqConfig | null;
|
|
20
|
+
/** Limpia la caché (tests o recarga en dev). */
|
|
21
|
+
declare function clearEcomiqConfigCache(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Identificador de tienda para settings (API / KV).
|
|
24
|
+
* 1. `slugStore` en `ecomiq.config.*` si existe.
|
|
25
|
+
* 2. Si no, deriva del header `Host` (`resolveStorefrontSettingsHost`).
|
|
26
|
+
*/
|
|
27
|
+
declare function resolvePublicStoreKey(hostHeader: string | null | undefined, cwd?: string): string;
|
|
28
|
+
declare function hasEcomiqSlugStoreConfig(cwd?: string): boolean;
|
|
29
|
+
|
|
30
|
+
export { type EcomiqConfig, EcomiqConfigSchema, clearEcomiqConfigCache, hasEcomiqSlugStoreConfig, loadEcomiqConfig, resolvePublicStoreKey };
|