@onlistify/storefront 0.1.0 → 0.1.1
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 +108 -17
- package/dist/cli.js +233 -0
- package/dist/index-CxOAQ9Nx.d.cts +53 -0
- package/dist/index-CxOAQ9Nx.d.ts +53 -0
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -55
- package/dist/index.d.ts +8 -55
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +139 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +26 -0
- package/dist/node.d.ts +26 -0
- package/dist/node.js +125 -0
- package/dist/node.js.map +1 -0
- package/dist/plugins/README.txt +24 -0
- package/dist/plugins/header-plugin.css +58 -0
- package/dist/plugins/header-plugin.js +72 -0
- package/dist/plugins/header-plugin.schema.json +45 -0
- package/dist/plugins/hero-plugin.css +34 -0
- package/dist/plugins/hero-plugin.js +56 -0
- package/dist/plugins/hero-plugin.schema.json +38 -0
- package/package.json +16 -2
package/README.md
CHANGED
|
@@ -1,41 +1,132 @@
|
|
|
1
1
|
# ONlistify Storefront
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
SDK para integrar el listado y la ficha de propiedades de una inmobiliaria en cualquier web. Una sola llamada monta tema, filtros, paginación y bloques configurados.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Instalación
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install @onlistify/storefront
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
El paquete incluye el **SDK** (navegador) y la **CLI**. Desde cualquier carpeta donde tengas un `.onlistify`:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @onlistify/storefront
|
|
15
|
+
npx onlistify pull --output-dir ./mi-sitio --env draft
|
|
16
|
+
npx onlistify push
|
|
17
|
+
npx onlistify promote
|
|
18
|
+
npx onlistify preview --env draft
|
|
19
|
+
npx onlistify dev
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Sin instalar (npx descarga y ejecuta): `npx @onlistify/storefront pull --output-dir ./mi-sitio --env draft`
|
|
23
|
+
|
|
24
|
+
Ejemplo mínimo de `.onlistify`: solo `apiUrl` y `apiKey`. El webhook se llama a `apiUrl/webhook`. Opcional: `storefrontUrl`, `tenantSlug` (para preview).
|
|
25
|
+
|
|
26
|
+
## Integración (cualquier URL)
|
|
8
27
|
|
|
9
|
-
|
|
28
|
+
Indica el **slug** del tenant y la **base** de la API. La ruta de tu página puede ser la que quieras (`/`, `/propiedades`, dominio propio, etc.):
|
|
10
29
|
|
|
11
30
|
```ts
|
|
12
|
-
import { bootSiteRuntime } from 'onlistify
|
|
31
|
+
import { bootSiteRuntime } from '@onlistify/storefront';
|
|
13
32
|
|
|
14
33
|
const app = document.getElementById('app');
|
|
15
34
|
if (app) {
|
|
16
|
-
await bootSiteRuntime(app
|
|
35
|
+
await bootSiteRuntime(app, {
|
|
36
|
+
slug: 'mi-inmobiliaria',
|
|
37
|
+
baseUrl: 'https://api.onlistify.com',
|
|
38
|
+
});
|
|
17
39
|
}
|
|
18
40
|
```
|
|
19
41
|
|
|
20
|
-
|
|
42
|
+
Con bundler (Vite, Webpack, etc.) o como módulo en el navegador. No hace falta usar una ruta tipo `/tienda/:slug`.
|
|
43
|
+
|
|
44
|
+
## Opciones
|
|
45
|
+
|
|
46
|
+
| Opción | Descripción |
|
|
47
|
+
|---------------|-------------|
|
|
48
|
+
| `slug` | Slug del tenant (recomendado). Sin esto, se puede usar `getSlugFromPath`. |
|
|
49
|
+
| `baseUrl` | Base de la API (ej. `https://api.onlistify.com`). |
|
|
50
|
+
| `environment` | `'draft'` \| `'live'`. Por defecto `'live'` o `?environment=draft` en la URL. |
|
|
51
|
+
| `getSlugFromPath` | Función `(pathname) => string` para obtener el slug desde la ruta. Se ignora si se pasa `slug`. |
|
|
52
|
+
| `onError` | Callback cuando falle la carga de configuración o el runtime. |
|
|
53
|
+
|
|
54
|
+
## App de desarrollo
|
|
55
|
+
|
|
56
|
+
En este repo, la app Vite usa variables de entorno para no depender de la ruta:
|
|
57
|
+
|
|
58
|
+
- `VITE_PUBLIC_STOREFRONT_SLUG` — slug del tenant (ej. `richistate`).
|
|
59
|
+
- `VITE_PUBLIC_API_URL` — base de la API.
|
|
60
|
+
|
|
61
|
+
Crea `.env` con esos valores y arranca con `pnpm dev`. La app funciona en la raíz (ej. `http://localhost:5174/`).
|
|
62
|
+
|
|
63
|
+
## Conectar a ONlistify para enviar cambios
|
|
64
|
+
|
|
65
|
+
El **storefront solo lee** (config, propiedades, plugins). Para **enviar cambios** (editar tema, bloques, publicar) tienes dos opciones:
|
|
66
|
+
|
|
67
|
+
**1. Dashboard (recomendado)**
|
|
68
|
+
- En el repo `onlistify-front` crea o edita `.env` con la URL de tu backend:
|
|
69
|
+
```bash
|
|
70
|
+
NEXT_PUBLIC_API_URL=https://tu-backend.onlistify.com
|
|
71
|
+
```
|
|
72
|
+
(En local: `http://localhost:8888` si el backend corre ahí.)
|
|
73
|
+
- Arranca el dashboard (`pnpm dev`), inicia sesión y entra en **Configuración del sitio** o **API pública**. Los cambios se envían al backend desde la UI.
|
|
74
|
+
|
|
75
|
+
**2. API con clave**
|
|
76
|
+
- En el dashboard ve a **API pública** y crea una API key del tenant.
|
|
77
|
+
- Desde scripts o integraciones:
|
|
78
|
+
```bash
|
|
79
|
+
export ONLISTIFY_API_URL=https://tu-backend.onlistify.com
|
|
80
|
+
export ONLISTIFY_API_KEY=onl_tu_clave
|
|
81
|
+
```
|
|
82
|
+
- `GET` config: `curl -H "X-API-Key: $ONLISTIFY_API_KEY" "$ONLISTIFY_API_URL/api/v1/public/site-config?environment=draft"`
|
|
83
|
+
- `PUT` para guardar borrador: body JSON con la config completa.
|
|
84
|
+
- `POST .../site-config/promote` para publicar borrador a vivo.
|
|
85
|
+
|
|
86
|
+
El **storefront** solo necesita `baseUrl` (la misma URL del backend) y `slug` del tenant para mostrar datos; no envía cambios.
|
|
87
|
+
|
|
88
|
+
## Build del SDK
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pnpm run build:sdk
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Salida en `dist/`: entrada principal (browser), entrada `node` (Node) y carpeta `dist/plugins/` con los plugins por defecto (hero, header).
|
|
95
|
+
|
|
96
|
+
## SDK Node (descargar web, push, webhook)
|
|
97
|
+
|
|
98
|
+
Para scripts o la CLI (descargar la web como código, subir cambios, disparar un webhook):
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import {
|
|
102
|
+
pullSiteConfig,
|
|
103
|
+
pushSiteConfig,
|
|
104
|
+
promoteSiteConfig,
|
|
105
|
+
exportSiteProject,
|
|
106
|
+
triggerWebhook,
|
|
107
|
+
getDefaultPluginsDir,
|
|
108
|
+
} from '@onlistify/storefront/node';
|
|
109
|
+
```
|
|
21
110
|
|
|
22
|
-
|
|
111
|
+
- `pullSiteConfig({ apiUrl, apiKey }, 'draft' | 'live')` — devuelve la config normalizada.
|
|
112
|
+
- `pushSiteConfig({ apiUrl, apiKey }, config)` — guarda borrador.
|
|
113
|
+
- `promoteSiteConfig({ apiUrl, apiKey })` — publica a live.
|
|
114
|
+
- `exportSiteProject({ apiUrl, apiKey }, { outputDir, environment?, includePlugins? })` — escribe en `outputDir` el `site-config.draft.json` y, si `includePlugins` es true, copia los plugins por defecto del SDK a `outputDir/plugins/`. Así los devs obtienen la config y el código físico de los bloques para editarlos.
|
|
115
|
+
- `triggerWebhook(url, payload?)` — POST a la URL (útil tras push/promote para desplegar).
|
|
116
|
+
- `getDefaultPluginsDir()` — ruta a `dist/plugins` del paquete.
|
|
23
117
|
|
|
24
|
-
-
|
|
25
|
-
- **Entorno:** `bootSiteRuntime(app, { environment: 'draft' | 'live' })`. Por defecto usa `?environment=draft` en la URL o `live`.
|
|
26
|
-
- **Slug custom:** `bootSiteRuntime(app, { getSlugFromPath: (pathname) => 'mi-tenant' })`.
|
|
27
|
-
- **Errores:** `bootSiteRuntime(app, { onError: (err) => console.error(err) })`.
|
|
118
|
+
La CLI incluida en el paquete (`npx onlistify`) usa este entry: `pull --output-dir ./mi-sitio` exporta el sitio completo; `push` / `promote` envían cambios y, si está definido `webhookUrl` en `.onlistify`, se llama al terminar.
|
|
28
119
|
|
|
29
|
-
|
|
120
|
+
## API pública (browser)
|
|
30
121
|
|
|
31
|
-
- `bootSiteRuntime(app, options?)` — monta
|
|
122
|
+
- `bootSiteRuntime(app, options?)` — monta el storefront en el elemento.
|
|
32
123
|
- `fetchSiteConfig(slug, environment?)` — configuración del sitio.
|
|
33
124
|
- `fetchProperties(slug, params?)` — listado de propiedades.
|
|
34
125
|
- `fetchPlugins(slug)` — plugins/bloques.
|
|
35
126
|
- `setApiBase(url)` — define la base de la API.
|
|
36
127
|
- Tipos: `SiteConfig`, `BlockDef`, `PageDef`, `PublicPropertySummary`, `ListPropertiesParams`, `BootSiteRuntimeOptions`, `StorefrontConfigError`, `StorefrontNetworkError`, etc.
|
|
37
128
|
|
|
38
|
-
|
|
129
|
+
## Errores
|
|
39
130
|
|
|
40
|
-
- `StorefrontConfigError`:
|
|
41
|
-
- `StorefrontNetworkError`:
|
|
131
|
+
- `StorefrontConfigError`: error HTTP de config o propiedades (`status` opcional).
|
|
132
|
+
- `StorefrontNetworkError`: error de red (`cause` con el error original).
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
async function loadSdk() {
|
|
10
|
+
try {
|
|
11
|
+
return await import(path.join(__dirname, 'node.js'));
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function loadConfig() {
|
|
18
|
+
const cwd = process.cwd();
|
|
19
|
+
let apiUrl = process.env.ONLISTIFY_API_URL;
|
|
20
|
+
let apiKey = process.env.ONLISTIFY_API_KEY;
|
|
21
|
+
let tenantSlug = process.env.ONLISTIFY_TENANT_SLUG;
|
|
22
|
+
let storefrontUrl = process.env.ONLISTIFY_STOREFRONT_URL;
|
|
23
|
+
const configPath = path.join(cwd, '.onlistify');
|
|
24
|
+
if (fs.existsSync(configPath)) {
|
|
25
|
+
try {
|
|
26
|
+
const data = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
27
|
+
apiUrl = apiUrl ?? data.apiUrl ?? data.ONLISTIFY_API_URL;
|
|
28
|
+
apiKey = apiKey ?? data.apiKey ?? data.ONLISTIFY_API_KEY;
|
|
29
|
+
tenantSlug = tenantSlug ?? data.tenantSlug ?? data.ONLISTIFY_TENANT_SLUG;
|
|
30
|
+
storefrontUrl = storefrontUrl ?? data.storefrontUrl ?? data.ONLISTIFY_STOREFRONT_URL;
|
|
31
|
+
} catch (_) {}
|
|
32
|
+
}
|
|
33
|
+
apiUrl = (apiUrl || 'http://localhost:8888').replace(/\/$/, '');
|
|
34
|
+
storefrontUrl = (storefrontUrl || 'http://localhost:5174').replace(/\/$/, '');
|
|
35
|
+
const webhookUrl = `${apiUrl}/webhook`;
|
|
36
|
+
return { apiUrl, apiKey, tenantSlug, storefrontUrl, webhookUrl };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function usage() {
|
|
40
|
+
console.log(`onlistify (@onlistify/storefront)
|
|
41
|
+
pull [--env draft|live] [--output FILE] Descarga config a archivo
|
|
42
|
+
pull --output-dir DIR [--env draft] Descarga sitio completo (config + plugins)
|
|
43
|
+
push [FILE] Sube site-config.draft.json como borrador
|
|
44
|
+
promote Publica borrador a live
|
|
45
|
+
preview [--env draft|live] Abre storefront en el navegador
|
|
46
|
+
dev [--config FILE] Watch y push al guardar
|
|
47
|
+
|
|
48
|
+
Config: .onlistify con apiUrl y apiKey (opcional: storefrontUrl, tenantSlug para preview). Webhook = apiUrl/webhook.
|
|
49
|
+
`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function api(apiUrl, apiKey, method, pathname, body = null) {
|
|
53
|
+
const url = `${apiUrl}/api/v1/public${pathname}`;
|
|
54
|
+
const opts = { method, headers: { 'X-API-Key': apiKey } };
|
|
55
|
+
if (body) {
|
|
56
|
+
opts.headers['Content-Type'] = 'application/json';
|
|
57
|
+
opts.body = typeof body === 'string' ? body : JSON.stringify(body);
|
|
58
|
+
}
|
|
59
|
+
const res = await fetch(url, opts);
|
|
60
|
+
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
|
|
61
|
+
return res.json();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function parseArgs(args) {
|
|
65
|
+
const out = { env: 'draft', output: null, outputDir: null, file: null, config: null };
|
|
66
|
+
for (let i = 0; i < args.length; i++) {
|
|
67
|
+
if (args[i] === '--env' && args[i + 1]) out.env = args[++i];
|
|
68
|
+
else if (args[i] === '--output' && args[i + 1]) out.output = args[++i];
|
|
69
|
+
else if (args[i] === '--output-dir' && args[i + 1]) out.outputDir = args[++i];
|
|
70
|
+
else if (args[i] === '--config' && args[i + 1]) out.config = args[++i];
|
|
71
|
+
else if (!args[i].startsWith('-')) { out.file = args[i]; break; }
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function cmdPull(config, args) {
|
|
77
|
+
const { apiUrl, apiKey } = config;
|
|
78
|
+
if (!apiKey) {
|
|
79
|
+
console.error('Falta ONLISTIFY_API_KEY o apiKey en .onlistify');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
const { env, output, outputDir } = parseArgs(args);
|
|
83
|
+
const envParam = env === 'live' ? 'live' : 'draft';
|
|
84
|
+
const sdk = await loadSdk();
|
|
85
|
+
|
|
86
|
+
if (outputDir && sdk?.exportSiteProject) {
|
|
87
|
+
const { configPath, pluginsPath } = await sdk.exportSiteProject(
|
|
88
|
+
{ apiUrl, apiKey },
|
|
89
|
+
{ outputDir, environment: envParam, includePlugins: true }
|
|
90
|
+
);
|
|
91
|
+
console.log('Sitio exportado:', configPath, pluginsPath ? `plugins: ${pluginsPath}` : '');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (sdk?.pullSiteConfig) {
|
|
96
|
+
const siteConfig = await sdk.pullSiteConfig({ apiUrl, apiKey }, envParam);
|
|
97
|
+
const outPath = path.resolve(process.cwd(), output || (envParam === 'live' ? 'site-config.live.json' : 'site-config.draft.json'));
|
|
98
|
+
fs.writeFileSync(outPath, JSON.stringify(siteConfig, null, 2), 'utf8');
|
|
99
|
+
console.log('Config guardada en', outPath);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const data = await api(apiUrl, apiKey, 'GET', `/site-config?environment=${envParam}`);
|
|
104
|
+
const json = JSON.stringify(data?.data ?? data ?? { pages: [] }, null, 2);
|
|
105
|
+
const outPath = path.resolve(process.cwd(), output || (envParam === 'live' ? 'site-config.live.json' : 'site-config.draft.json'));
|
|
106
|
+
fs.writeFileSync(outPath, json, 'utf8');
|
|
107
|
+
console.log('Config guardada en', outPath);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function cmdPush(config, args) {
|
|
111
|
+
const { apiUrl, apiKey, webhookUrl } = config;
|
|
112
|
+
if (!apiKey) {
|
|
113
|
+
console.error('Falta ONLISTIFY_API_KEY o apiKey en .onlistify');
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
const { file } = parseArgs(args);
|
|
117
|
+
const configPath = path.resolve(process.cwd(), file || 'site-config.draft.json');
|
|
118
|
+
let body;
|
|
119
|
+
try {
|
|
120
|
+
body = fs.readFileSync(configPath, 'utf8');
|
|
121
|
+
JSON.parse(body);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
if (e.code === 'ENOENT') console.error('Archivo no encontrado:', configPath);
|
|
124
|
+
else console.error('JSON inválido:', e.message);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const sdk = await loadSdk();
|
|
129
|
+
if (sdk?.pushSiteConfig) {
|
|
130
|
+
await sdk.pushSiteConfig({ apiUrl, apiKey }, JSON.parse(body));
|
|
131
|
+
} else {
|
|
132
|
+
await api(apiUrl, apiKey, 'PUT', '/site-config?environment=draft', body);
|
|
133
|
+
}
|
|
134
|
+
console.log('Borrador guardado');
|
|
135
|
+
if (webhookUrl && sdk?.triggerWebhook) {
|
|
136
|
+
try {
|
|
137
|
+
await sdk.triggerWebhook(webhookUrl, { event: 'onlistify.push' });
|
|
138
|
+
console.log('Webhook llamado');
|
|
139
|
+
} catch (e) {
|
|
140
|
+
console.warn('Webhook:', e.message);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function cmdPromote(config) {
|
|
146
|
+
const { apiUrl, apiKey, webhookUrl } = config;
|
|
147
|
+
if (!apiKey) {
|
|
148
|
+
console.error('Falta ONLISTIFY_API_KEY o apiKey en .onlistify');
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
const sdk = await loadSdk();
|
|
152
|
+
if (sdk?.promoteSiteConfig) {
|
|
153
|
+
await sdk.promoteSiteConfig({ apiUrl, apiKey });
|
|
154
|
+
} else {
|
|
155
|
+
await api(apiUrl, apiKey, 'POST', '/site-config/promote');
|
|
156
|
+
}
|
|
157
|
+
console.log('Config publicada en vivo');
|
|
158
|
+
if (webhookUrl && sdk?.triggerWebhook) {
|
|
159
|
+
try {
|
|
160
|
+
await sdk.triggerWebhook(webhookUrl, { event: 'onlistify.promote' });
|
|
161
|
+
console.log('Webhook llamado');
|
|
162
|
+
} catch (e) {
|
|
163
|
+
console.warn('Webhook:', e.message);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function cmdPreview(config, args) {
|
|
169
|
+
const { storefrontUrl, tenantSlug } = config;
|
|
170
|
+
const { env } = parseArgs(args);
|
|
171
|
+
const slug = tenantSlug || 'mi-inmobiliaria';
|
|
172
|
+
const envParam = env === 'live' ? '' : '?environment=draft';
|
|
173
|
+
const url = `${storefrontUrl}/tienda/${encodeURIComponent(slug)}${envParam}`;
|
|
174
|
+
const start = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
175
|
+
spawn(start, [url], { stdio: 'ignore', detached: true });
|
|
176
|
+
console.log('Abriendo', url);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function cmdDev(config, args) {
|
|
180
|
+
const { apiUrl, apiKey, webhookUrl } = config;
|
|
181
|
+
if (!apiKey) {
|
|
182
|
+
console.error('Falta ONLISTIFY_API_KEY o apiKey en .onlistify');
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
const { config: configFile } = parseArgs(args);
|
|
186
|
+
const configPath = path.resolve(process.cwd(), configFile || 'site-config.draft.json');
|
|
187
|
+
if (!fs.existsSync(configPath)) {
|
|
188
|
+
console.error('Archivo no encontrado:', configPath);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
const sdk = await loadSdk();
|
|
192
|
+
const push = async () => {
|
|
193
|
+
try {
|
|
194
|
+
const body = fs.readFileSync(configPath, 'utf8');
|
|
195
|
+
const parsed = JSON.parse(body);
|
|
196
|
+
if (sdk?.pushSiteConfig) await sdk.pushSiteConfig({ apiUrl, apiKey }, parsed);
|
|
197
|
+
else await api(apiUrl, apiKey, 'PUT', '/site-config?environment=draft', body);
|
|
198
|
+
console.log('[%s] Borrador actualizado', new Date().toISOString());
|
|
199
|
+
if (webhookUrl && sdk?.triggerWebhook) {
|
|
200
|
+
try { await sdk.triggerWebhook(webhookUrl, { event: 'onlistify.push' }); } catch (_) {}
|
|
201
|
+
}
|
|
202
|
+
} catch (e) {
|
|
203
|
+
console.error('Error:', e.message);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
fs.watch(configPath, (eventType) => {
|
|
207
|
+
if (eventType === 'change') push();
|
|
208
|
+
});
|
|
209
|
+
console.log('Observando', configPath, '- Recarga la pestaña de preview al cambiar.');
|
|
210
|
+
await push();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const commands = { pull: cmdPull, push: cmdPush, promote: cmdPromote, preview: cmdPreview, dev: cmdDev };
|
|
214
|
+
const cmd = process.argv[2];
|
|
215
|
+
const cmdArgs = process.argv.slice(3);
|
|
216
|
+
|
|
217
|
+
if (!cmd || cmd === '-h' || cmd === '--help') {
|
|
218
|
+
usage();
|
|
219
|
+
process.exit(0);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const fn = commands[cmd];
|
|
223
|
+
if (!fn) {
|
|
224
|
+
console.error('Comando desconocido:', cmd);
|
|
225
|
+
usage();
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const config = loadConfig();
|
|
230
|
+
Promise.resolve(fn(config, cmdArgs)).catch((e) => {
|
|
231
|
+
console.error(e.message);
|
|
232
|
+
process.exit(1);
|
|
233
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type SiteEnvironment = 'draft' | 'live';
|
|
2
|
+
interface SiteConfigTheme {
|
|
3
|
+
primaryColor?: string;
|
|
4
|
+
accentColor?: string;
|
|
5
|
+
logoUrl?: string;
|
|
6
|
+
customCssVars?: Record<string, string>;
|
|
7
|
+
}
|
|
8
|
+
interface PropiedadCardBlockProps {
|
|
9
|
+
theme?: {
|
|
10
|
+
primaryColor?: string;
|
|
11
|
+
accentColor?: string;
|
|
12
|
+
badgeBackground?: string;
|
|
13
|
+
cardBorderRadius?: string;
|
|
14
|
+
imageAspectRatio?: '16/9' | '4/3' | '1/1';
|
|
15
|
+
};
|
|
16
|
+
showFields?: Record<string, boolean>;
|
|
17
|
+
badges?: {
|
|
18
|
+
featured?: {
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
label?: string;
|
|
21
|
+
};
|
|
22
|
+
virtualTour?: {
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
label?: string;
|
|
25
|
+
};
|
|
26
|
+
custom?: Array<{
|
|
27
|
+
key: string;
|
|
28
|
+
label: string;
|
|
29
|
+
when?: string;
|
|
30
|
+
}>;
|
|
31
|
+
};
|
|
32
|
+
layout?: 'compact' | 'default' | 'minimal';
|
|
33
|
+
linkTarget?: '_self' | '_blank';
|
|
34
|
+
openDetailIn?: 'same' | 'modal' | 'drawer';
|
|
35
|
+
}
|
|
36
|
+
interface BlockDef<TProps = Record<string, unknown>> {
|
|
37
|
+
id: string;
|
|
38
|
+
type: string;
|
|
39
|
+
props?: TProps;
|
|
40
|
+
}
|
|
41
|
+
interface PageDef {
|
|
42
|
+
id: string;
|
|
43
|
+
path: string;
|
|
44
|
+
blocks: BlockDef[];
|
|
45
|
+
}
|
|
46
|
+
interface SiteConfig {
|
|
47
|
+
version?: number;
|
|
48
|
+
theme?: SiteConfigTheme;
|
|
49
|
+
themeOverrides?: Record<string, unknown>;
|
|
50
|
+
pages?: PageDef[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type { BlockDef as B, PageDef as P, SiteEnvironment as S, SiteConfig as a, PropiedadCardBlockProps as b, SiteConfigTheme as c };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type SiteEnvironment = 'draft' | 'live';
|
|
2
|
+
interface SiteConfigTheme {
|
|
3
|
+
primaryColor?: string;
|
|
4
|
+
accentColor?: string;
|
|
5
|
+
logoUrl?: string;
|
|
6
|
+
customCssVars?: Record<string, string>;
|
|
7
|
+
}
|
|
8
|
+
interface PropiedadCardBlockProps {
|
|
9
|
+
theme?: {
|
|
10
|
+
primaryColor?: string;
|
|
11
|
+
accentColor?: string;
|
|
12
|
+
badgeBackground?: string;
|
|
13
|
+
cardBorderRadius?: string;
|
|
14
|
+
imageAspectRatio?: '16/9' | '4/3' | '1/1';
|
|
15
|
+
};
|
|
16
|
+
showFields?: Record<string, boolean>;
|
|
17
|
+
badges?: {
|
|
18
|
+
featured?: {
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
label?: string;
|
|
21
|
+
};
|
|
22
|
+
virtualTour?: {
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
label?: string;
|
|
25
|
+
};
|
|
26
|
+
custom?: Array<{
|
|
27
|
+
key: string;
|
|
28
|
+
label: string;
|
|
29
|
+
when?: string;
|
|
30
|
+
}>;
|
|
31
|
+
};
|
|
32
|
+
layout?: 'compact' | 'default' | 'minimal';
|
|
33
|
+
linkTarget?: '_self' | '_blank';
|
|
34
|
+
openDetailIn?: 'same' | 'modal' | 'drawer';
|
|
35
|
+
}
|
|
36
|
+
interface BlockDef<TProps = Record<string, unknown>> {
|
|
37
|
+
id: string;
|
|
38
|
+
type: string;
|
|
39
|
+
props?: TProps;
|
|
40
|
+
}
|
|
41
|
+
interface PageDef {
|
|
42
|
+
id: string;
|
|
43
|
+
path: string;
|
|
44
|
+
blocks: BlockDef[];
|
|
45
|
+
}
|
|
46
|
+
interface SiteConfig {
|
|
47
|
+
version?: number;
|
|
48
|
+
theme?: SiteConfigTheme;
|
|
49
|
+
themeOverrides?: Record<string, unknown>;
|
|
50
|
+
pages?: PageDef[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type { BlockDef as B, PageDef as P, SiteEnvironment as S, SiteConfig as a, PropiedadCardBlockProps as b, SiteConfigTheme as c };
|
package/dist/index.cjs
CHANGED
|
@@ -526,7 +526,8 @@ PropiedadCard = __decorateClass([
|
|
|
526
526
|
|
|
527
527
|
// src/sdk/site-runtime.ts
|
|
528
528
|
var BUILTIN_PLUGIN_URLS = {
|
|
529
|
-
"onl-hero": { js: "/plugins/example/hero-plugin.js", css: "/plugins/example/hero-plugin.css" }
|
|
529
|
+
"onl-hero": { js: "/plugins/example/hero-plugin.js", css: "/plugins/example/hero-plugin.css" },
|
|
530
|
+
"onl-header": { js: "/plugins/example/header-plugin.js", css: "/plugins/example/header-plugin.css" }
|
|
530
531
|
};
|
|
531
532
|
var loadedPlugins = /* @__PURE__ */ new Set();
|
|
532
533
|
function getSlugFromPath(pathname) {
|
|
@@ -700,10 +701,9 @@ async function bootSiteRuntime(app, options = {}) {
|
|
|
700
701
|
} else {
|
|
701
702
|
setApiBase(getApiBase());
|
|
702
703
|
}
|
|
703
|
-
const
|
|
704
|
-
const slug = getSlug(typeof window !== "undefined" ? window.location.pathname : "");
|
|
704
|
+
const slug = options.slug?.trim() || (options.getSlugFromPath ?? getSlugFromPath)(typeof window !== "undefined" ? window.location.pathname : "");
|
|
705
705
|
if (!slug) {
|
|
706
|
-
app.innerHTML = "<p>
|
|
706
|
+
app.innerHTML = "<p>Indica el tenant con <code>slug</code> en opciones (recomendado) o con <code>getSlugFromPath</code>. Ej: bootSiteRuntime(app, { slug: 'mi-inmobiliaria', baseUrl: 'https://api.ejemplo.com' })</p>";
|
|
707
707
|
return;
|
|
708
708
|
}
|
|
709
709
|
app.innerHTML = "<p>Cargando...</p>";
|