@ecomiq/storefront 2.0.17 → 2.0.18

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.17",
3
+ "version": "2.0.18",
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",
@@ -36,6 +36,7 @@
36
36
  "./internal/page": "./src/runtime/page.tsx",
37
37
  "./internal/context": "./src/runtime/context.ts",
38
38
  "./internal/server-config": "./src/server-config-store.ts",
39
+ "./internal/health": "./src/health.ts",
39
40
  "./internal/head": "./src/runtime/head.ts",
40
41
  "./internal/default-layout": "./src/runtime/default-layout.tsx",
41
42
  "./internal/checkout": "./src/pages/checkout.tsx",
@@ -78,7 +79,7 @@
78
79
  "vite": "^8.1.0",
79
80
  "wrangler": "^4.100.0",
80
81
  "zod": "^3.25.0",
81
- "@ecomiq/core": "^0.1.5"
82
+ "@ecomiq/core": "^0.1.6"
82
83
  },
83
84
  "peerDependencies": {
84
85
  "react": "^19.0.0",
package/src/codegen.mjs CHANGED
@@ -155,15 +155,18 @@ export async function generateApp({ cwd, appDir, frameworkRoot, config }) {
155
155
  HEADER +
156
156
  `import startServer, { createServerEntry } from '@tanstack/react-start/server-entry'\n` +
157
157
  `import config from '../../ecomiq.config.js'\n` +
158
- `import { registerServerConfig } from '@ecomiq/storefront/internal/server-config'\n\n` +
158
+ `import { registerServerConfig } from '@ecomiq/storefront/internal/server-config'\n` +
159
+ `import { handleEcomiqHealth } from '@ecomiq/storefront/internal/health'\n\n` +
159
160
  `export default createServerEntry({\n` +
160
- ` async fetch(...args) {\n` +
161
+ ` async fetch(request, ...rest) {\n` +
161
162
  ` let api = config.api\n` +
162
163
  ` if (import.meta.env.DEV && config.dev?.apiBaseUrl) {\n` +
163
164
  ` api = { ...api, baseUrl: config.dev.apiBaseUrl }\n` +
164
165
  ` }\n` +
165
166
  ` registerServerConfig({ domain: config.domain, api })\n` +
166
- ` return startServer.fetch(...args)\n` +
167
+ ` const health = await handleEcomiqHealth(request)\n` +
168
+ ` if (health) return health\n` +
169
+ ` return startServer.fetch(request, ...rest)\n` +
167
170
  ` },\n` +
168
171
  `})\n`,
169
172
  )
package/src/health.ts ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Health interno del runtime storefront.
3
+ * GET /ping → liveness puro: "ok" 200 (target del probe de vm-manager).
4
+ * GET /__ecomiq/health → proceso up + estado de /v1/settings (sin SSR de páginas).
5
+ */
6
+ import { getStorefrontClient } from './client'
7
+
8
+ export const ECOMIQ_HEALTH_PATH = '/__ecomiq/health'
9
+ export const ECOMIQ_PING_PATH = '/ping'
10
+
11
+ export type EcomiqHealthSettingsStatus = {
12
+ ok: boolean
13
+ latencyMs: number
14
+ storeId?: string
15
+ domain?: string | null
16
+ error?: string
17
+ }
18
+
19
+ export type EcomiqHealthResponse = {
20
+ ok: true
21
+ settings: EcomiqHealthSettingsStatus
22
+ }
23
+
24
+ async function probeSettings(): Promise<EcomiqHealthSettingsStatus> {
25
+ const started = Date.now()
26
+ try {
27
+ const settings = await getStorefrontClient().getSettings()
28
+ return {
29
+ ok: true,
30
+ latencyMs: Date.now() - started,
31
+ storeId: settings.store.id,
32
+ domain: settings.store.domain ?? settings.store.slug,
33
+ }
34
+ } catch (error) {
35
+ return {
36
+ ok: false,
37
+ latencyMs: Date.now() - started,
38
+ error: error instanceof Error ? error.message : String(error),
39
+ }
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Si el request es al health interno, responde JSON.
45
+ * Si no, `null` para que el caller siga con el fetch normal de TanStack.
46
+ */
47
+ export async function handleEcomiqHealth(
48
+ request: Request,
49
+ ): Promise<Response | null> {
50
+ const { pathname } = new URL(request.url)
51
+ if (pathname !== ECOMIQ_HEALTH_PATH && pathname !== ECOMIQ_PING_PATH) {
52
+ return null
53
+ }
54
+
55
+ if (request.method !== 'GET' && request.method !== 'HEAD') {
56
+ return new Response(null, {
57
+ status: 405,
58
+ headers: { allow: 'GET, HEAD' },
59
+ })
60
+ }
61
+
62
+ if (pathname === ECOMIQ_PING_PATH) {
63
+ return new Response(request.method === 'HEAD' ? null : 'ok', {
64
+ status: 200,
65
+ headers: {
66
+ 'content-type': 'text/plain; charset=utf-8',
67
+ 'cache-control': 'no-store',
68
+ },
69
+ })
70
+ }
71
+
72
+ const body: EcomiqHealthResponse = {
73
+ ok: true,
74
+ settings: await probeSettings(),
75
+ }
76
+
77
+ if (request.method === 'HEAD') {
78
+ return new Response(null, {
79
+ status: 200,
80
+ headers: { 'content-type': 'application/json; charset=utf-8' },
81
+ })
82
+ }
83
+
84
+ return Response.json(body, {
85
+ status: 200,
86
+ headers: {
87
+ 'cache-control': 'no-store',
88
+ },
89
+ })
90
+ }