create-email-renderer 0.1.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 ADDED
@@ -0,0 +1,119 @@
1
+ # create-email-renderer
2
+
3
+ Renderiza plantillas de email por bloques a **HTML email-safe sin React**. JS puro: funciona en Node, Cloudflare Workers, Vercel Edge, Deno y Bun — solo manipulación de strings, nada que compilar ni ejecutar en un runtime especial.
4
+
5
+ Es el compañero de [`create-email-template`](https://www.npmjs.com/package/create-email-template) (editor visual). Comparten el mismo core: los tipos, los defaults y la normalización son idénticos en ambos lados, por lo que el preview del editor y el HTML final nunca divergen.
6
+
7
+ ## Instalación
8
+
9
+ ```sh
10
+ npm install create-email-renderer
11
+ ```
12
+
13
+ Sin React, sin DOM, sin peer dependencies. Única dependencia: `sanitize-html`.
14
+
15
+ ## `renderTemplateEmail` — el que usarás el 95% del tiempo
16
+
17
+ Toma el payload JSON guardado desde el editor y devuelve el HTML final con las variables resueltas:
18
+
19
+ ```ts
20
+ import { renderTemplateEmail } from "create-email-renderer";
21
+
22
+ const { html, subject } = await renderTemplateEmail({
23
+ subject: "Hola {firstName}, tu webinar es el {webinarDate}",
24
+ payload: template.payload, // string JSON u objeto { content, settings }
25
+ context: { // tus datos reales
26
+ firstName: user.name,
27
+ webinarDate: "12 de octubre",
28
+ registerUrl: "https://mi-sitio.com/registro",
29
+ },
30
+ });
31
+
32
+ await emailClient.send({ to: user.email, subject, html });
33
+ ```
34
+
35
+ ### Opciones
36
+
37
+ | Opción | Tipo | Default | Descripción |
38
+ |---|---|---|---|
39
+ | `subject` | `string` | — (obligatorio) | Asunto del correo. **Resuelve variables**: `"Hola {firstName}"` → `"Hola Danny"`. También queda en `<title>` y en el preview oculto del email. |
40
+ | `payload` | `string \| object \| null` | — | El JSON del editor. Tolera `null`/`undefined`/string inválido (devuelve vacío). |
41
+ | `context` | `EmailContext` | `SAMPLE_CONTEXT` | Valores para las variables `{key}`. Se mezcla sobre `SAMPLE_CONTEXT`, así que los correos de prueba siempre se ven completos aunque falten claves. |
42
+ | `settings` | `Partial<EmailSettings>` | settings del payload | Sobrescribe `{ pageBackground, cardBorderWidth, cardBorderRadius }` guardados. |
43
+ | `palette` | `EmailPalette` | `DEFAULT_PALETTE` | Sobrescribe los colores: `{ INK, DARK, GOLD, CREAM_DIM }`. |
44
+
45
+ **Comportamiento**: lanza `Template payload has no blocks` si tras normalizar no queda ningún bloque. En cualquier otro caso de datos sucios, normaliza sin quejarse (ver [Normalización](#normalización-de-payloads)).
46
+
47
+ ## `renderEmailHtml` — bloques ya parseados
48
+
49
+ Si tú ya tienes los bloques normalizados y quieres el HTML directo:
50
+
51
+ ```ts
52
+ import { renderEmailHtml } from "create-email-renderer";
53
+
54
+ const html = await renderEmailHtml({
55
+ blocks: [
56
+ { id: "1", type: "header", props: { brandName: "Mi Marca" } },
57
+ { id: "2", type: "button", props: { label: "Ver más", href: "{link}" } },
58
+ ],
59
+ subject: "Bienvenida {firstName}",
60
+ context: { firstName: "Ana", link: "https://mi-sitio.com" },
61
+ // settings?: Partial<EmailSettings>
62
+ // palette?: EmailPalette
63
+ });
64
+ ```
65
+
66
+ ## Variables disponibles por defecto
67
+
68
+ Cualquier texto del editor acepta `{key}`. Estas son las incluidas en `DEFAULT_VARIABLES`:
69
+
70
+ **Base:** `name`, `firstName`, `lastName`, `email`, `phone`, `role`, `companyName`, `unsubscribeUrl`, `link`, `date`, `year`, `siteUrl`, `supportEmail`
71
+
72
+ **Contextuales:** `webinarName`, `webinarDate`, `webinarTime`, `webinarDuration`, `webinarUrl`, `registerUrl`, `liveUrl`, `slotsLeft`, `reminderDate`, `webinarHost`, `completeRegistrationUrl`, `resetPasswordUrl`, `confirmEmailUrl`
73
+
74
+ Las resuelves pasando `context`; las claves que falten se mezclan con `SAMPLE_CONTEXT` (valores de ejemplo). Para usar otras, define tus propias variables en el editor (`config.variables`) y pásalas aquí en el `context`.
75
+
76
+ ## Normalización de payloads
77
+
78
+ Los payloads guardados con versiones anteriores del esquema (props que ya no existen, campos faltantes, tipos desconocidos, ids perdidos) se **auto-reparan**. `parseTemplatePayload` normaliza automáticamente, y puedes usar las funciones directamente:
79
+
80
+ ```ts
81
+ import { normalizeBlocks, normalizeSettings } from "create-email-renderer";
82
+
83
+ const blocks = normalizeBlocks(jsonMigrado.content, miBlockLibraryCustom?);
84
+ // → descarta tipos desconocidos, completa props con defaults del esquema actual,
85
+ // repara ids faltantes y coacciona tipos ("17" → 17). Nunca lanza.
86
+ ```
87
+
88
+ Esto significa que **añadir props nuevas a un bloque no rompe las plantillas ya guardadas**: se auto-completan al cargarse o renderizarse, sin script de migración.
89
+
90
+ ## Otros helpers
91
+
92
+ | Export | Descripción |
93
+ |---|---|
94
+ | `parseTemplatePayload(raw)` | `string \| object \| null` → `{ content, settings }` normalizado. Nunca lanza. |
95
+ | `buildTemplateContext(base, extra?)` | Limpia `null`/`""` de `extra` y mezcla sobre `SAMPLE_CONTEXT`. |
96
+ | `resolveVariables(text, ctx)` | Reemplaza `{key}` en un texto. |
97
+ | `extractVariables(text)` / `validateVariables(text, knownKeys)` | Inspección de variables usadas/no reconocidas. |
98
+ | `renderRichText(html)` | Sanitiza y aplica estilos inline al rich text del editor. |
99
+ | `sanitizeRichText` / `escapeHtml` / `normalizeBlockHtml` / `isRichText` | Piezas del pipeline por separado. |
100
+ | `createBlock(type, library)` | Crea un bloque nuevo con defaultProps (útil para generar plantillas por código). |
101
+ | Defaults | `DEFAULT_BLOCK_LIBRARY`, `DEFAULT_PALETTE`, `DEFAULT_SETTINGS`, `DEFAULT_VARIABLES`, `SAMPLE_CONTEXT`. |
102
+
103
+ ## Subpaths
104
+
105
+ Todo está disponible desde la raíz. Si prefieres imports granulares (menos bundle en serverless):
106
+
107
+ ```
108
+ create-email-renderer/server → parseTemplateEmail, renderTemplateEmail, buildTemplateContext
109
+ create-email-renderer/html-render → renderEmailHtml
110
+ create-email-renderer/normalize → normalizeBlocks, normalizeSettings
111
+ create-email-renderer/types → todos los tipos + createBlock
112
+ create-email-renderer/variables → variables, contextos y resolve
113
+ create-email-renderer/richtext → helpers de rich text
114
+ create-email-renderer/default-blocks → defaults de bloques, paleta y settings
115
+ ```
116
+
117
+ ## Licencia
118
+
119
+ MIT
@@ -0,0 +1,5 @@
1
+ import type { BlockDefinition, EmailPalette, EmailSettings } from "./types.js";
2
+ export declare const DEFAULT_PALETTE: EmailPalette;
3
+ export declare const DEFAULT_SETTINGS: EmailSettings;
4
+ export declare const DEFAULT_BLOCK_LIBRARY: BlockDefinition[];
5
+ //# sourceMappingURL=default-blocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-blocks.d.ts","sourceRoot":"","sources":["../src/default-blocks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE9E,eAAO,MAAM,eAAe,EAAE,YAK7B,CAAA;AAED,eAAO,MAAM,gBAAgB,EAAE,aAI9B,CAAA;AAED,eAAO,MAAM,qBAAqB,EAAE,eAAe,EAiJlD,CAAA"}
@@ -0,0 +1,159 @@
1
+ import { newId } from "./id.js";
2
+ export const DEFAULT_PALETTE = {
3
+ INK: "#221d15",
4
+ DARK: "#0d0b08",
5
+ GOLD: "#d7b227",
6
+ CREAM_DIM: "#b8ae9d",
7
+ };
8
+ export const DEFAULT_SETTINGS = {
9
+ pageBackground: "#f5f1e8",
10
+ cardBorderWidth: 1,
11
+ cardBorderRadius: 4,
12
+ };
13
+ export const DEFAULT_BLOCK_LIBRARY = [
14
+ {
15
+ type: "header",
16
+ label: "Header / Marca",
17
+ description: "Logo y nombre de la marca",
18
+ defaultProps: {
19
+ brandName: "Tu Marca",
20
+ tagline: "Breve descripción de tu marca",
21
+ align: "center",
22
+ backgroundColor: "#0d0b08",
23
+ paddingY: 24,
24
+ paddingX: 32,
25
+ },
26
+ },
27
+ {
28
+ type: "hero",
29
+ label: "Hero",
30
+ description: "Titular grande + subtítulo",
31
+ defaultProps: {
32
+ title: "¡Un gran titular aquí!",
33
+ subtitle: "Un subtítulo de apoyo para tu mensaje.",
34
+ align: "center",
35
+ paddingY: 40,
36
+ paddingX: 32,
37
+ },
38
+ },
39
+ {
40
+ type: "heading",
41
+ label: "Título",
42
+ description: "Título de sección",
43
+ defaultProps: {
44
+ text: "Título de sección",
45
+ size: 22,
46
+ color: "#0d0b08",
47
+ align: "left",
48
+ paddingY: 16,
49
+ paddingX: 32,
50
+ },
51
+ },
52
+ {
53
+ type: "text",
54
+ label: "Texto",
55
+ description: "Párrafo de texto",
56
+ defaultProps: {
57
+ text: "Escribe aquí tu mensaje. Puedes usar variables como {name}.",
58
+ size: 15,
59
+ color: "#221d15",
60
+ align: "left",
61
+ paddingY: 12,
62
+ paddingX: 32,
63
+ },
64
+ },
65
+ {
66
+ type: "list",
67
+ label: "Lista",
68
+ description: "Viñetas de beneficios",
69
+ defaultProps: {
70
+ items: ["Beneficio 1", "Beneficio 2", "Beneficio 3"],
71
+ icon: "✓",
72
+ align: "left",
73
+ paddingY: 12,
74
+ paddingX: 32,
75
+ },
76
+ },
77
+ {
78
+ type: "button",
79
+ label: "Botón CTA",
80
+ description: "Botón con enlace",
81
+ defaultProps: {
82
+ label: "¡Quiero mi cupo!",
83
+ href: "",
84
+ backgroundColor: "#d7b227",
85
+ color: "#0d0b08",
86
+ align: "center",
87
+ paddingY: 16,
88
+ paddingX: 32,
89
+ },
90
+ },
91
+ {
92
+ type: "image",
93
+ label: "Imagen",
94
+ description: "Imagen del correo",
95
+ defaultProps: {
96
+ src: "",
97
+ alt: "Imagen",
98
+ width: 480,
99
+ align: "center",
100
+ paddingY: 16,
101
+ paddingX: 32,
102
+ },
103
+ },
104
+ {
105
+ type: "quote",
106
+ label: "Cita / Testimonio",
107
+ description: "Cita con autor",
108
+ defaultProps: {
109
+ text: "Esto cambió por completo mi manera de pensar.",
110
+ author: "Nombre del participante",
111
+ borderColor: "#d7b227",
112
+ align: "left",
113
+ paddingY: 16,
114
+ paddingX: 32,
115
+ },
116
+ },
117
+ {
118
+ type: "columns",
119
+ label: "Columnas",
120
+ description: "Dos columnas de texto",
121
+ defaultProps: {
122
+ columns: [
123
+ { id: newId(), text: "Columna 1" },
124
+ { id: newId(), text: "Columna 2" },
125
+ ],
126
+ align: "left",
127
+ paddingY: 16,
128
+ paddingX: 32,
129
+ },
130
+ },
131
+ {
132
+ type: "divider",
133
+ label: "Divisor",
134
+ description: "Línea separadora",
135
+ defaultProps: {
136
+ color: "#e3dccb",
137
+ paddingY: 8,
138
+ paddingX: 32,
139
+ },
140
+ },
141
+ {
142
+ type: "spacer",
143
+ label: "Espaciador",
144
+ description: "Altura en píxeles",
145
+ defaultProps: {
146
+ height: 24,
147
+ },
148
+ },
149
+ {
150
+ type: "footer",
151
+ label: "Footer",
152
+ description: "Legal y baja de suscripción",
153
+ defaultProps: {
154
+ text: "Recibes este correo por estar registrado en nuestra plataforma.",
155
+ brandName: "Tu Marca",
156
+ },
157
+ },
158
+ ];
159
+ //# sourceMappingURL=default-blocks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-blocks.js","sourceRoot":"","sources":["../src/default-blocks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { EmailBlock, EmailContext, EmailPalette, EmailSettings } from "./types.js";
2
+ export interface RenderEmailHtmlOptions {
3
+ blocks: EmailBlock[];
4
+ subject?: string;
5
+ context?: EmailContext;
6
+ settings?: Partial<EmailSettings>;
7
+ palette?: EmailPalette;
8
+ }
9
+ /**
10
+ * Renderiza el payload de bloques a HTML email-safe sin React.
11
+ * Equivalente funcional de `renderEmailHtml` basado en react-email.
12
+ */
13
+ export declare const renderEmailHtml: ({ blocks, subject, context, settings, palette, }: RenderEmailHtmlOptions) => Promise<string>;
14
+ //# sourceMappingURL=html-render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-render.d.ts","sourceRoot":"","sources":["../src/html-render.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAIV,UAAU,EAGV,YAAY,EACZ,YAAY,EACZ,aAAa,EAUd,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAoVD;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAU,kDAMnC,sBAAsB,KAAG,OAAO,CAAC,MAAM,CA4BzC,CAAC"}
@@ -0,0 +1,248 @@
1
+ // Serializador puro JSON -> HTML email-safe. Es el equivalente sin React de
2
+ // EmailTemplate (blocks.tsx): mismas estructuras (tablas + estilos inline),
3
+ // mismas variables, mismo richtext. Debe mantenerse visualmente sincronizado.
4
+ import { escapeHtml, renderRichText } from "./richtext.js";
5
+ import { DEFAULT_PALETTE, DEFAULT_SETTINGS } from "./default-blocks.js";
6
+ import { resolveVariables, SAMPLE_CONTEXT } from "./variables.js";
7
+ const alignMap = {
8
+ left: "left",
9
+ center: "center",
10
+ right: "right",
11
+ };
12
+ // Props CSS que aceptan números sin unidad (como los inline styles de React).
13
+ const UNITLESS = new Set([
14
+ "font-weight",
15
+ "line-height",
16
+ "opacity",
17
+ "z-index",
18
+ "flex",
19
+ "flex-grow",
20
+ "flex-shrink",
21
+ "flex-basis",
22
+ "order",
23
+ "column-count",
24
+ "animation-iteration-count",
25
+ "orphans",
26
+ "widows",
27
+ "zoom",
28
+ ]);
29
+ const styleToString = (style) => Object.entries(style)
30
+ .filter(([, v]) => v !== undefined && v !== null && v !== "")
31
+ .map(([k, v]) => {
32
+ const prop = k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
33
+ // Los números llevan px salvo las props unitless (igual que React).
34
+ const value = typeof v === "number" && v !== 0 && !UNITLESS.has(prop)
35
+ ? `${v}px`
36
+ : String(v);
37
+ return `${prop}:${value}`;
38
+ })
39
+ .join(";");
40
+ const blockPadding = (props) => {
41
+ const py = "paddingY" in props ? (props.paddingY ?? 0) : 0;
42
+ const px = "paddingX" in props ? (props.paddingX ?? 0) : 0;
43
+ return { padding: `${py}px ${px}px` };
44
+ };
45
+ /** Tabla full-width con una celda: equivalente email-safe de <Section>. */
46
+ const section = (style, content) => `<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td style="${styleToString(style)}">${content}</td></tr></tbody></table>`;
47
+ const text = (content, style) => `<p style="${styleToString(style)}">${content}</p>`;
48
+ const renderHeader = (props, context, palette) => {
49
+ const logo = props.logoUrl
50
+ ? `<img src="${escapeHtml(resolveVariables(props.logoUrl, context))}" alt="${escapeHtml(props.brandName)}" width="140" style="display:inline-block;border:0;outline:none;" />`
51
+ : `<div style="${styleToString({ color: palette.GOLD, fontSize: 20, fontWeight: 700, letterSpacing: "1px", margin: 0 })}">${renderRichText(resolveVariables(props.brandName, context))}</div>`;
52
+ const tagline = props.tagline
53
+ ? `<div style="${styleToString({ color: palette.CREAM_DIM, fontSize: 11, margin: "6px 0 0", textTransform: "uppercase", letterSpacing: "1.5px" })}">${renderRichText(resolveVariables(props.tagline, context))}</div>`
54
+ : "";
55
+ return section({
56
+ backgroundColor: props.backgroundColor || palette.DARK,
57
+ textAlign: alignMap[props.align || "center"],
58
+ ...blockPadding(props),
59
+ }, logo + tagline);
60
+ };
61
+ const renderHero = (props, context, palette) => {
62
+ const image = props.imageUrl
63
+ ? `<img src="${escapeHtml(resolveVariables(props.imageUrl, context))}" alt="" width="480" style="max-width:100%;margin:0 auto 24px;display:block;border:0;" />`
64
+ : "";
65
+ const title = `<div style="${styleToString({ color: palette.DARK, fontSize: 32, lineHeight: 1.2, margin: "0 0 12px", fontWeight: 700 })}">${renderRichText(resolveVariables(props.title, context))}</div>`;
66
+ const subtitle = props.subtitle
67
+ ? `<div style="${styleToString({ color: palette.INK, fontSize: 16, lineHeight: 1.6, margin: 0 })}">${renderRichText(resolveVariables(props.subtitle, context))}</div>`
68
+ : "";
69
+ return section({
70
+ textAlign: alignMap[props.align || "center"],
71
+ ...blockPadding(props),
72
+ ...(props.backgroundColor
73
+ ? { backgroundColor: props.backgroundColor }
74
+ : {}),
75
+ }, image + title + subtitle);
76
+ };
77
+ const renderHeading = (props, context, palette) => section({
78
+ textAlign: alignMap[props.align || "left"],
79
+ ...blockPadding(props),
80
+ ...(props.backgroundColor
81
+ ? { backgroundColor: props.backgroundColor }
82
+ : {}),
83
+ }, `<div style="${styleToString({ color: props.color || palette.DARK, fontSize: props.size ?? 22, lineHeight: 1.3, margin: 0, fontWeight: 700 })}">${renderRichText(resolveVariables(props.text, context))}</div>`);
84
+ const renderText = (props, context, palette) => section({
85
+ textAlign: alignMap[props.align || "left"],
86
+ ...blockPadding(props),
87
+ ...(props.backgroundColor
88
+ ? { backgroundColor: props.backgroundColor }
89
+ : {}),
90
+ }, `<div style="${styleToString({ color: props.color || palette.INK, fontSize: props.size || 15, lineHeight: 1.6, margin: 0 })}">${renderRichText(resolveVariables(props.text, context))}</div>`);
91
+ const renderList = (props, context, palette) => {
92
+ const items = props.items
93
+ .map((item) => `<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-bottom:8px;"><tbody><tr><td width="24" valign="top">${text(escapeHtml(props.icon || "✓"), { color: palette.GOLD, fontWeight: 700, margin: 0, fontSize: 14 })}</td><td valign="top"><div style="${styleToString({ color: palette.INK, fontSize: 14, lineHeight: 1.5, margin: 0 })}">${renderRichText(resolveVariables(item, context))}</div></td></tr></tbody></table>`)
94
+ .join("");
95
+ return section({
96
+ ...blockPadding(props),
97
+ ...(props.backgroundColor
98
+ ? { backgroundColor: props.backgroundColor }
99
+ : {}),
100
+ }, items);
101
+ };
102
+ const renderButton = (props, context, palette) => section({
103
+ textAlign: alignMap[props.align || "center"],
104
+ ...blockPadding(props),
105
+ ...(props.blockBackgroundColor
106
+ ? { backgroundColor: props.blockBackgroundColor }
107
+ : {}),
108
+ }, `<a href="${escapeHtml(resolveVariables(props.href, context))}" target="_blank" style="${styleToString({ backgroundColor: props.backgroundColor || palette.GOLD, color: props.color || palette.DARK, fontWeight: 700, padding: "14px 28px", borderRadius: 999, fontSize: 14, textTransform: "uppercase", letterSpacing: "0.5px", display: "inline-block", textDecoration: "none" })}">${escapeHtml(resolveVariables(props.label, context))}</a>`);
109
+ const renderImage = (props, context, palette) => {
110
+ let content;
111
+ if (props.src) {
112
+ const img = `<img src="${escapeHtml(resolveVariables(props.src, context))}" alt="${escapeHtml(props.alt || "")}" width="${props.width || 480}" style="max-width:100%;display:inline-block;border:0;" />`;
113
+ content = props.href
114
+ ? `<a href="${escapeHtml(resolveVariables(props.href, context))}" target="_blank" rel="noopener">${img}</a>`
115
+ : img;
116
+ }
117
+ else {
118
+ content = text("(Selecciona una imagen)", {
119
+ color: palette.CREAM_DIM,
120
+ fontSize: 12,
121
+ margin: 0,
122
+ });
123
+ }
124
+ return section({
125
+ textAlign: alignMap[props.align || "center"],
126
+ ...blockPadding(props),
127
+ ...(props.backgroundColor
128
+ ? { backgroundColor: props.backgroundColor }
129
+ : {}),
130
+ }, content);
131
+ };
132
+ const renderQuote = (props, context, palette) => {
133
+ const quote = `<div style="${styleToString({ color: palette.INK, fontStyle: "italic", fontSize: 15, lineHeight: 1.6, margin: 0 })}">\u201C${renderRichText(resolveVariables(props.text, context))}\u201D</div>`;
134
+ const author = props.author
135
+ ? `<div style="${styleToString({ color: palette.CREAM_DIM, fontSize: 12, margin: "8px 0 0" })}">\u2014 ${renderRichText(resolveVariables(props.author, context))}</div>`
136
+ : "";
137
+ return section({
138
+ borderLeft: `${props.borderWidth ?? 3}px solid ${props.borderColor || palette.GOLD}`,
139
+ ...blockPadding(props),
140
+ paddingLeft: (props.paddingX ?? 32) + 16,
141
+ ...(props.marginLeft ? { marginLeft: `${props.marginLeft}px` } : {}),
142
+ ...(props.backgroundColor
143
+ ? { backgroundColor: props.backgroundColor }
144
+ : {}),
145
+ }, quote + author);
146
+ };
147
+ const renderColumns = (props, context, palette) => {
148
+ const cols = (props.columns || [])
149
+ .map((col) => `<td width="50%" valign="top" style="width:50%;padding-right:8px;"><div style="${styleToString({ color: palette.INK, fontSize: 14, lineHeight: 1.5, margin: 0 })}">${renderRichText(resolveVariables(col.text, context))}</div></td>`)
150
+ .join("");
151
+ return section({
152
+ ...blockPadding(props),
153
+ ...(props.backgroundColor
154
+ ? { backgroundColor: props.backgroundColor }
155
+ : {}),
156
+ }, `<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr>${cols}</tr></tbody></table>`);
157
+ };
158
+ const renderDivider = (props) => {
159
+ const style = props.height !== undefined
160
+ ? { padding: `${props.height}px ${props.paddingX ?? 32}px` }
161
+ : blockPadding(props);
162
+ return section({
163
+ ...style,
164
+ ...(props.backgroundColor
165
+ ? { backgroundColor: props.backgroundColor }
166
+ : {}),
167
+ }, `<hr style="width:100%;border:none;border-top:1px solid ${props.color || "#e3dccb"};margin:0;" />`);
168
+ };
169
+ const renderSpacer = (props) => section({
170
+ height: props.height || 24,
171
+ lineHeight: 0,
172
+ fontSize: 0,
173
+ ...(props.backgroundColor
174
+ ? { backgroundColor: props.backgroundColor }
175
+ : {}),
176
+ }, "&nbsp;");
177
+ const renderFooter = (props, context, palette) => {
178
+ const body = text(escapeHtml(props.text
179
+ ? resolveVariables(props.text, context)
180
+ : "Recibes este correo por estar registrado en nuestra plataforma."), { color: "#f5f1e8", fontSize: 12, lineHeight: 1.6, margin: "0 0 12px" });
181
+ const brand = props.brandName
182
+ ? `<div style="${styleToString({ color: palette.GOLD, fontSize: 11, letterSpacing: "1px", margin: "12px 0 0" })}">${renderRichText(resolveVariables(props.brandName, context))}</div>`
183
+ : "";
184
+ return section({
185
+ backgroundColor: props.backgroundColor || palette.DARK,
186
+ padding: "24px 32px",
187
+ textAlign: "center",
188
+ }, body + brand);
189
+ };
190
+ const renderBlock = (block, context, palette) => {
191
+ switch (block.type) {
192
+ case "header":
193
+ return renderHeader(block.props, context, palette);
194
+ case "hero":
195
+ return renderHero(block.props, context, palette);
196
+ case "heading":
197
+ return renderHeading(block.props, context, palette);
198
+ case "text":
199
+ return renderText(block.props, context, palette);
200
+ case "list":
201
+ return renderList(block.props, context, palette);
202
+ case "button":
203
+ return renderButton(block.props, context, palette);
204
+ case "image":
205
+ return renderImage(block.props, context, palette);
206
+ case "quote":
207
+ return renderQuote(block.props, context, palette);
208
+ case "columns":
209
+ return renderColumns(block.props, context, palette);
210
+ case "divider":
211
+ return renderDivider(block.props);
212
+ case "spacer":
213
+ return renderSpacer(block.props);
214
+ case "footer":
215
+ return renderFooter(block.props, context, palette);
216
+ default:
217
+ return "";
218
+ }
219
+ };
220
+ /**
221
+ * Renderiza el payload de bloques a HTML email-safe sin React.
222
+ * Equivalente funcional de `renderEmailHtml` basado en react-email.
223
+ */
224
+ export const renderEmailHtml = async ({ blocks, subject = "", context = SAMPLE_CONTEXT, settings = {}, palette = DEFAULT_PALETTE, }) => {
225
+ const pageBackground = settings.pageBackground ?? DEFAULT_SETTINGS.pageBackground;
226
+ const cardBorderWidth = settings.cardBorderWidth ?? DEFAULT_SETTINGS.cardBorderWidth;
227
+ const cardBorderRadius = settings.cardBorderRadius ?? DEFAULT_SETTINGS.cardBorderRadius;
228
+ const resolvedSubject = resolveVariables(subject, context);
229
+ const body = blocks
230
+ .map((block) => renderBlock(block, context, palette))
231
+ .join("");
232
+ return `<!DOCTYPE html>
233
+ <html dir="ltr" lang="es">
234
+ <head>
235
+ <meta charset="utf-8" />
236
+ <meta name="viewport" content="width=device-width" />
237
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
238
+ <title>${escapeHtml(resolvedSubject)}</title>
239
+ </head>
240
+ <body style="${styleToString({ margin: 0, padding: 0, backgroundColor: pageBackground })}">
241
+ <div style="display:none;font-size:1px;line-height:1px;max-height:0;max-width:0;opacity:0;overflow:hidden;">${escapeHtml(resolvedSubject)}</div>
242
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="${styleToString({ maxWidth: 600, width: "100%", backgroundColor: "#ffffff", border: `${cardBorderWidth}px solid #e3dccb`, borderRadius: cardBorderRadius, margin: "0 auto" })}">
243
+ <tbody><tr><td>${body}</td></tr></tbody>
244
+ </table>
245
+ </body>
246
+ </html>`;
247
+ };
248
+ //# sourceMappingURL=html-render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-render.js","sourceRoot":"","sources":["../src/html-render.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,4EAA4E;AAC5E,8EAA8E;AAC9E,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AA8BlE,MAAM,QAAQ,GAAG;IACf,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,OAAO;CACN,CAAC;AAEX,8EAA8E;AAC9E,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC;IACvB,aAAa;IACb,aAAa;IACb,SAAS;IACT,SAAS;IACT,MAAM;IACN,WAAW;IACX,aAAa;IACb,YAAY;IACZ,OAAO;IACP,cAAc;IACd,2BAA2B;IAC3B,SAAS;IACT,QAAQ;IACR,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,CACpB,KAAkD,EAC1C,EAAE,CACV,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;KAClB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;KAC5D,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;IACd,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC/D,oEAAoE;IACpE,MAAM,KAAK,GACT,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;QACrD,CAAC,CAAC,GAAG,CAAC,IAAI;QACV,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAChB,OAAO,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC;AAC5B,CAAC,CAAC;KACD,IAAI,CAAC,GAAG,CAAC,CAAC;AAEf,MAAM,YAAY,GAAG,CAAC,KAAsB,EAAE,EAAE;IAC9C,MAAM,EAAE,GAAG,UAAU,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,MAAM,EAAE,GAAG,UAAU,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACxC,CAAC,CAAC;AAEF,2EAA2E;AAC3E,MAAM,OAAO,GAAG,CACd,KAAkD,EAClD,OAAe,EACP,EAAE,CACV,4GAA4G,aAAa,CAAC,KAAK,CAAC,KAAK,OAAO,4BAA4B,CAAC;AAE3K,MAAM,IAAI,GAAG,CACX,OAAe,EACf,KAAkD,EAC1C,EAAE,CAAC,aAAa,aAAa,CAAC,KAAK,CAAC,KAAK,OAAO,MAAM,CAAC;AAEjE,MAAM,YAAY,GAAG,CACnB,KAAkB,EAClB,OAAqB,EACrB,OAAqB,EACb,EAAE;IACV,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO;QACxB,CAAC,CAAC,aAAa,UAAU,CAAC,gBAAgB,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,UAAU,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,sEAAsE;QAC9K,CAAC,CAAC,eAAe,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,cAAc,CAAC,gBAAgB,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC;IACjM,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO;QAC3B,CAAC,CAAC,eAAe,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,KAAK,cAAc,CAAC,gBAAgB,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,QAAQ;QACtN,CAAC,CAAC,EAAE,CAAC;IACP,OAAO,OAAO,CACZ;QACE,eAAe,EAAE,KAAK,CAAC,eAAe,IAAI,OAAO,CAAC,IAAI;QACtD,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,QAAQ,CAAC;QAC5C,GAAG,YAAY,CAAC,KAAK,CAAC;KACvB,EACD,IAAI,GAAG,OAAO,CACf,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CACjB,KAAgB,EAChB,OAAqB,EACrB,OAAqB,EACb,EAAE;IACV,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ;QAC1B,CAAC,CAAC,aAAa,UAAU,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,2FAA2F;QAC/J,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,KAAK,GAAG,eAAe,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,KAAK,cAAc,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC;IAC3M,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ;QAC7B,CAAC,CAAC,eAAe,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,cAAc,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ;QACtK,CAAC,CAAC,EAAE,CAAC;IACP,OAAO,OAAO,CACZ;QACE,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,QAAQ,CAAC;QAC5C,GAAG,YAAY,CAAC,KAAK,CAAC;QACtB,GAAG,CAAC,KAAK,CAAC,eAAe;YACvB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE;YAC5C,CAAC,CAAC,EAAE,CAAC;KACR,EACD,KAAK,GAAG,KAAK,GAAG,QAAQ,CACzB,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CACpB,KAAmB,EACnB,OAAqB,EACrB,OAAqB,EACb,EAAE,CACV,OAAO,CACL;IACE,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC;IAC1C,GAAG,YAAY,CAAC,KAAK,CAAC;IACtB,GAAG,CAAC,KAAK,CAAC,eAAe;QACvB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE;QAC5C,CAAC,CAAC,EAAE,CAAC;CACR,EACD,eAAe,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,KAAK,cAAc,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,QAAQ,CAChN,CAAC;AAEJ,MAAM,UAAU,GAAG,CACjB,KAAgB,EAChB,OAAqB,EACrB,OAAqB,EACb,EAAE,CACV,OAAO,CACL;IACE,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC;IAC1C,GAAG,YAAY,CAAC,KAAK,CAAC;IACtB,GAAG,CAAC,KAAK,CAAC,eAAe;QACvB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE;QAC5C,CAAC,CAAC,EAAE,CAAC;CACR,EACD,eAAe,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,cAAc,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,QAAQ,CAC9L,CAAC;AAEJ,MAAM,UAAU,GAAG,CACjB,KAAgB,EAChB,OAAqB,EACrB,OAAqB,EACb,EAAE;IACV,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK;SACtB,GAAG,CACF,CAAC,IAAI,EAAE,EAAE,CACP,wJAAwJ,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,qCAAqC,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,cAAc,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,kCAAkC,CAC3c;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;IACZ,OAAO,OAAO,CACZ;QACE,GAAG,YAAY,CAAC,KAAK,CAAC;QACtB,GAAG,CAAC,KAAK,CAAC,eAAe;YACvB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE;YAC5C,CAAC,CAAC,EAAE,CAAC;KACR,EACD,KAAK,CACN,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CACnB,KAAkB,EAClB,OAAqB,EACrB,OAAqB,EACb,EAAE,CACV,OAAO,CACL;IACE,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,QAAQ,CAAC;IAC5C,GAAG,YAAY,CAAC,KAAK,CAAC;IACtB,GAAG,CAAC,KAAK,CAAC,oBAAoB;QAC5B,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,oBAAoB,EAAE;QACjD,CAAC,CAAC,EAAE,CAAC;CACR,EACD,YAAY,UAAU,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,4BAA4B,aAAa,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,IAAI,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,KAAK,UAAU,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,MAAM,CACjb,CAAC;AAEJ,MAAM,WAAW,GAAG,CAClB,KAAiB,EACjB,OAAqB,EACrB,OAAqB,EACb,EAAE;IACV,IAAI,OAAe,CAAC;IACpB,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QACd,MAAM,GAAG,GAAG,aAAa,UAAU,CAAC,gBAAgB,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,UAAU,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC,YAAY,KAAK,CAAC,KAAK,IAAI,GAAG,4DAA4D,CAAC;QACzM,OAAO,GAAG,KAAK,CAAC,IAAI;YAClB,CAAC,CAAC,YAAY,UAAU,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,oCAAoC,GAAG,MAAM;YAC5G,CAAC,CAAC,GAAG,CAAC;IACV,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,IAAI,CAAC,yBAAyB,EAAE;YACxC,KAAK,EAAE,OAAO,CAAC,SAAS;YACxB,QAAQ,EAAE,EAAE;YACZ,MAAM,EAAE,CAAC;SACV,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CACZ;QACE,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,QAAQ,CAAC;QAC5C,GAAG,YAAY,CAAC,KAAK,CAAC;QACtB,GAAG,CAAC,KAAK,CAAC,eAAe;YACvB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE;YAC5C,CAAC,CAAC,EAAE,CAAC;KACR,EACD,OAAO,CACR,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAClB,KAAiB,EACjB,OAAqB,EACrB,OAAqB,EACb,EAAE;IACV,MAAM,KAAK,GAAG,eAAe,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,WAAW,cAAc,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,cAAc,CAAC;IAChN,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM;QACzB,CAAC,CAAC,eAAe,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,YAAY,cAAc,CAAC,gBAAgB,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,QAAQ;QACxK,CAAC,CAAC,EAAE,CAAC;IACP,OAAO,OAAO,CACZ;QACE,UAAU,EAAE,GAAG,KAAK,CAAC,WAAW,IAAI,CAAC,YAAY,KAAK,CAAC,WAAW,IAAI,OAAO,CAAC,IAAI,EAAE;QACpF,GAAG,YAAY,CAAC,KAAK,CAAC;QACtB,WAAW,EAAE,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,GAAG,EAAE;QACxC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,GAAG,CAAC,KAAK,CAAC,eAAe;YACvB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE;YAC5C,CAAC,CAAC,EAAE,CAAC;KACR,EACD,KAAK,GAAG,MAAM,CACf,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CACpB,KAAmB,EACnB,OAAqB,EACrB,OAAqB,EACb,EAAE;IACV,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;SAC/B,GAAG,CACF,CAAC,GAAG,EAAE,EAAE,CACN,iFAAiF,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,cAAc,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,aAAa,CACxO;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;IACZ,OAAO,OAAO,CACZ;QACE,GAAG,YAAY,CAAC,KAAK,CAAC;QACtB,GAAG,CAAC,KAAK,CAAC,eAAe;YACvB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE;YAC5C,CAAC,CAAC,EAAE,CAAC;KACR,EACD,iGAAiG,IAAI,uBAAuB,CAC7H,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CAAC,KAAmB,EAAU,EAAE;IACpD,MAAM,KAAK,GACT,KAAK,CAAC,MAAM,KAAK,SAAS;QACxB,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,MAAM,MAAM,KAAK,CAAC,QAAQ,IAAI,EAAE,IAAI,EAAE;QAC5D,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAC1B,OAAO,OAAO,CACZ;QACE,GAAG,KAAK;QACR,GAAG,CAAC,KAAK,CAAC,eAAe;YACvB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE;YAC5C,CAAC,CAAC,EAAE,CAAC;KACR,EACD,0DAA0D,KAAK,CAAC,KAAK,IAAI,SAAS,gBAAgB,CACnG,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,KAAkB,EAAU,EAAE,CAClD,OAAO,CACL;IACE,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE;IAC1B,UAAU,EAAE,CAAC;IACb,QAAQ,EAAE,CAAC;IACX,GAAG,CAAC,KAAK,CAAC,eAAe;QACvB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE;QAC5C,CAAC,CAAC,EAAE,CAAC;CACR,EACD,QAAQ,CACT,CAAC;AAEJ,MAAM,YAAY,GAAG,CACnB,KAAkB,EAClB,OAAqB,EACrB,OAAqB,EACb,EAAE;IACV,MAAM,IAAI,GAAG,IAAI,CACf,UAAU,CACR,KAAK,CAAC,IAAI;QACR,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC;QACvC,CAAC,CAAC,iEAAiE,CACtE,EACD,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,CACxE,CAAC;IACF,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS;QAC3B,CAAC,CAAC,eAAe,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,KAAK,cAAc,CAAC,gBAAgB,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,QAAQ;QACtL,CAAC,CAAC,EAAE,CAAC;IACP,OAAO,OAAO,CACZ;QACE,eAAe,EAAE,KAAK,CAAC,eAAe,IAAI,OAAO,CAAC,IAAI;QACtD,OAAO,EAAE,WAAW;QACpB,SAAS,EAAE,QAAQ;KACpB,EACD,IAAI,GAAG,KAAK,CACb,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAClB,KAAiB,EACjB,OAAqB,EACrB,OAAqB,EACb,EAAE;IACV,QAAQ,KAAK,CAAC,IAAsB,EAAE,CAAC;QACrC,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC,KAAK,CAAC,KAAoB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACpE,KAAK,MAAM;YACT,OAAO,UAAU,CAAC,KAAK,CAAC,KAAkB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAChE,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC,KAAK,CAAC,KAAqB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACtE,KAAK,MAAM;YACT,OAAO,UAAU,CAAC,KAAK,CAAC,KAAkB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAChE,KAAK,MAAM;YACT,OAAO,UAAU,CAAC,KAAK,CAAC,KAAkB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAChE,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC,KAAK,CAAC,KAAoB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACpE,KAAK,OAAO;YACV,OAAO,WAAW,CAAC,KAAK,CAAC,KAAmB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAClE,KAAK,OAAO;YACV,OAAO,WAAW,CAAC,KAAK,CAAC,KAAmB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAClE,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC,KAAK,CAAC,KAAqB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACtE,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC,KAAK,CAAC,KAAqB,CAAC,CAAC;QACpD,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC,KAAK,CAAC,KAAoB,CAAC,CAAC;QAClD,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC,KAAK,CAAC,KAAoB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACpE;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,EACpC,MAAM,EACN,OAAO,GAAG,EAAE,EACZ,OAAO,GAAG,cAAc,EACxB,QAAQ,GAAG,EAAE,EACb,OAAO,GAAG,eAAe,GACF,EAAmB,EAAE;IAC5C,MAAM,cAAc,GAClB,QAAQ,CAAC,cAAc,IAAI,gBAAgB,CAAC,cAAc,CAAC;IAC7D,MAAM,eAAe,GACnB,QAAQ,CAAC,eAAe,IAAI,gBAAgB,CAAC,eAAe,CAAC;IAC/D,MAAM,gBAAgB,GACpB,QAAQ,CAAC,gBAAgB,IAAI,gBAAgB,CAAC,gBAAgB,CAAC;IAEjE,MAAM,eAAe,GAAG,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3D,MAAM,IAAI,GAAG,MAAM;SAChB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;SACpD,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO;;;;;;aAMI,UAAU,CAAC,eAAe,CAAC;;iBAEvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC;kHACwB,UAAU,CAAC,eAAe,CAAC;+GAC9B,aAAa,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,eAAe,kBAAkB,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;uBACnQ,IAAI;;;QAGnB,CAAC;AACT,CAAC,CAAC"}
package/dist/id.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare const newId: () => string;
2
+ //# sourceMappingURL=id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK,QAAO,MAA6B,CAAC"}
package/dist/id.js ADDED
@@ -0,0 +1,4 @@
1
+ // Identificador de bloques/columnas usando la API nativa del navegador,
2
+ // sin depender del paquete uuid.
3
+ export const newId = () => crypto.randomUUID();
4
+ //# sourceMappingURL=id.js.map
package/dist/id.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.js","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,iCAAiC;AACjC,MAAM,CAAC,MAAM,KAAK,GAAG,GAAW,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC"}
@@ -0,0 +1,8 @@
1
+ export * from "./types.js";
2
+ export * from "./variables.js";
3
+ export * from "./richtext.js";
4
+ export * from "./default-blocks.js";
5
+ export * from "./normalize.js";
6
+ export * from "./html-render.js";
7
+ export * from "./server.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ // Core puro (sin React): tipos, variables, richtext, bloques por defecto y
2
+ // serializador HTML. La UI de edición vive en create-email-template,
3
+ // que consume este paquete como única fuente de verdad del modelo.
4
+ export * from "./types.js";
5
+ export * from "./variables.js";
6
+ export * from "./richtext.js";
7
+ export * from "./default-blocks.js";
8
+ export * from "./normalize.js";
9
+ export * from "./html-render.js";
10
+ export * from "./server.js";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,qEAAqE;AACrE,mEAAmE;AACnE,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { BlockDefinition, EmailBlock, EmailSettings } from "./types.js";
2
+ /**
3
+ * Normaliza un array de bloques de origen desconocido (JSON de la BD, import,
4
+ * versión vieja del esquema): descarta tipos que no están en la librería,
5
+ * completa props con los defaults, repara ids faltantes y coacciona tipos.
6
+ * Nunca lanza.
7
+ */
8
+ export declare const normalizeBlocks: (input: unknown, library?: BlockDefinition[]) => EmailBlock[];
9
+ /** Normaliza los settings: solo claves conocidas, coacción numérica, defaults. */
10
+ export declare const normalizeSettings: (input: unknown) => EmailSettings;
11
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,aAAa,EACd,MAAM,YAAY,CAAC;AAoEpB;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAC1B,OAAO,OAAO,EACd,UAAS,eAAe,EAA0B,KACjD,UAAU,EAsBZ,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,aAmBlD,CAAC"}