@ecomiq/storefront 2.0.15 → 2.0.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecomiq/storefront",
3
- "version": "2.0.15",
3
+ "version": "2.0.17",
4
4
  "description": "Micro framework SSR de Ecomiq para storefronts. Páginas, datos y deploy sin configurar nada.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -17,7 +17,7 @@ import {
17
17
  signInWithEcomiqCredentials,
18
18
  type EcomiqSignInCredentials,
19
19
  } from '../auth/ecomiq-storefront-sign-in'
20
- import { useSettings } from './ecomiq'
20
+ import { AppEcomiqContext } from './ecomiq'
21
21
 
22
22
  export type CreateStorefrontAuthClientOptions = {
23
23
  baseURL?: string
@@ -55,7 +55,9 @@ export function useAuth() {
55
55
  if (!client) throw new Error('useAuth debe usarse dentro de AuthProvider')
56
56
 
57
57
  const sessionQuery = client.useSession()
58
- const settings = useSettings()
58
+ // Mismo módulo que AppEcomiqProvider (no useSettings del barrel) + soft read
59
+ // para no tumbar el árbol si hay re-render concurrente post-sesión.
60
+ const ecomiq = useContext(AppEcomiqContext)
59
61
 
60
62
  return {
61
63
  client,
@@ -63,7 +65,10 @@ export function useAuth() {
63
65
  isPending: sessionQuery.isPending,
64
66
  error: sessionQuery.error,
65
67
  signIn: async (credentials: EcomiqSignInCredentials) => {
66
- await signInWithEcomiqCredentials(client, settings, credentials)
68
+ if (!ecomiq) {
69
+ throw new Error('signIn requiere AppEcomiqProvider')
70
+ }
71
+ await signInWithEcomiqCredentials(client, ecomiq.settings, credentials)
67
72
  const result = await client.getSession()
68
73
  const session = (result as { data?: StorefrontSession | null })?.data ?? null
69
74
  if (!session?.user) {
@@ -1,14 +1,25 @@
1
1
  /**
2
- * Runtime interno de Ecomiq (solo lo importa el código generado en .ecomiq).
2
+ * Runtime interno de Ecomiq (solo lo importa el código generado en `.ecomiq`).
3
3
  * Traduce el contrato público `definePage` a las opciones del router interno.
4
4
  */
5
+ import { useContext } from 'react'
6
+ import { useRouterState } from '@tanstack/react-router'
5
7
  import type { StorefrontSettings } from '@ecomiq/core'
6
8
  import type { EcomiqPage, PublicEcomiqConfig } from '../index'
7
- import { useSettings } from '../providers'
9
+ import { AppEcomiqContext } from '../providers/ecomiq'
8
10
  import { serverApi } from '../server'
9
11
  import { StorefrontError } from '../ui/storefront-error'
10
12
  import { buildPageHead } from './head'
11
13
 
14
+ function isSettings(value: unknown): value is StorefrontSettings {
15
+ return Boolean(
16
+ value &&
17
+ typeof value === 'object' &&
18
+ value !== null &&
19
+ 'store' in value,
20
+ )
21
+ }
22
+
12
23
  async function resolveSettingsForMeta(
13
24
  matches?: Array<{ loaderData?: unknown }>,
14
25
  ): Promise<StorefrontSettings | undefined> {
@@ -16,8 +27,8 @@ async function resolveSettingsForMeta(
16
27
  // En SSR `matches` suele venir stale (updateMatch no muta el array), así que
17
28
  // caemos a serverApi: el cliente core cachea settings y no hay HTTP extra.
18
29
  const fromRoot = matches?.[0]?.loaderData
19
- if (fromRoot && typeof fromRoot === 'object' && fromRoot !== null && 'store' in fromRoot) {
20
- return fromRoot as StorefrontSettings
30
+ if (isSettings(fromRoot)) {
31
+ return fromRoot
21
32
  }
22
33
  try {
23
34
  return await serverApi.getSettings()
@@ -26,6 +37,18 @@ async function resolveSettingsForMeta(
26
37
  }
27
38
  }
28
39
 
40
+ function usePageSettings(): StorefrontSettings | undefined {
41
+ // Mismo módulo que AppEcomiqProvider (no el barrel) para evitar contextos duplicados.
42
+ const fromProvider = useContext(AppEcomiqContext)?.settings
43
+ const fromRootLoader = useRouterState({
44
+ select: (state) => {
45
+ const data = state.matches[0]?.loaderData
46
+ return isSettings(data) ? data : undefined
47
+ },
48
+ })
49
+ return fromProvider ?? fromRootLoader
50
+ }
51
+
29
52
  export function pageToRouteOptions<
30
53
  TData,
31
54
  TParams extends Record<string, string>,
@@ -108,7 +131,13 @@ export function PageBoundary<
108
131
  params: TParams
109
132
  search: TSearch
110
133
  }) {
111
- const settings = useSettings()
134
+ const settings = usePageSettings()
135
+ // Concurrent render / contexto duplicado: no tumbar el root.
136
+ // El re-render sync con provider o el loader del root suelen recuperar.
137
+ if (!settings) {
138
+ const Loading = page.loading
139
+ return Loading ? <Loading /> : null
140
+ }
112
141
  const Component = page.component
113
142
  return <Component data={data} params={params} search={search} settings={settings} />
114
143
  }
@@ -198,12 +198,20 @@ export function createViteConfig({ appDir, config, projectRoot, runtime }) {
198
198
  configFile: false,
199
199
  publicDir: resolve(appDir, '../public'),
200
200
  resolve: {
201
+ // Evita dos copias de React/context: el template resuelve desde
202
+ // node_modules del proyecto y `.ecomiq` desde symlinks — sin dedupe,
203
+ // AppEcomiqProvider y useSettings no comparten el mismo createContext
204
+ // (falla al re-render post useSession de Better Auth).
205
+ // No aliasar `@ecomiq/storefront` → rompe subpaths (`/internal/*`).
201
206
  dedupe: [
202
207
  'react',
203
208
  'react-dom',
209
+ '@ecomiq/storefront',
210
+ '@ecomiq/core',
204
211
  '@tanstack/react-router',
205
212
  '@tanstack/react-start',
206
213
  '@tanstack/react-query',
214
+ 'better-auth',
207
215
  ],
208
216
  },
209
217
  plugins,
@@ -223,6 +231,10 @@ export function createViteConfig({ appDir, config, projectRoot, runtime }) {
223
231
  },
224
232
  },
225
233
  server: {
234
+ // `true` → escucha en 0.0.0.0 y muestra Network URL (IP local), no solo localhost.
235
+ // Override: `dev.host: false | 'localhost' | string` en ecomiq.config.
236
+ host: config?.dev?.host ?? true,
237
+ allowedHosts: config?.dev?.host ?? true,
226
238
  fs: { allow },
227
239
  ...(resolvedHttps.https ? { https: resolvedHttps.https } : {}),
228
240
  },