@sn4p.dev/mucv 0.5.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 +126 -0
- package/dist/core/elementor-parser.d.ts +22 -0
- package/dist/core/elementor-parser.d.ts.map +1 -0
- package/dist/core/elementor-parser.js +186 -0
- package/dist/core/elementor-parser.js.map +1 -0
- package/dist/core/html-parser.d.ts +27 -0
- package/dist/core/html-parser.d.ts.map +1 -0
- package/dist/core/html-parser.js +247 -0
- package/dist/core/html-parser.js.map +1 -0
- package/dist/core/jsx-parser.d.ts +22 -0
- package/dist/core/jsx-parser.d.ts.map +1 -0
- package/dist/core/jsx-parser.js +261 -0
- package/dist/core/jsx-parser.js.map +1 -0
- package/dist/core/mapper.d.ts +52 -0
- package/dist/core/mapper.d.ts.map +1 -0
- package/dist/core/mapper.js +141 -0
- package/dist/core/mapper.js.map +1 -0
- package/dist/core/normalizer.d.ts +57 -0
- package/dist/core/normalizer.d.ts.map +1 -0
- package/dist/core/normalizer.js +285 -0
- package/dist/core/normalizer.js.map +1 -0
- package/dist/core/optimizer.d.ts +21 -0
- package/dist/core/optimizer.d.ts.map +1 -0
- package/dist/core/optimizer.js +75 -0
- package/dist/core/optimizer.js.map +1 -0
- package/dist/core/parse-result.d.ts +10 -0
- package/dist/core/parse-result.d.ts.map +1 -0
- package/dist/core/parse-result.js +6 -0
- package/dist/core/parse-result.js.map +1 -0
- package/dist/core/vir.d.ts +136 -0
- package/dist/core/vir.d.ts.map +1 -0
- package/dist/core/vir.js +9 -0
- package/dist/core/vir.js.map +1 -0
- package/dist/generators/style-serializer.d.ts +25 -0
- package/dist/generators/style-serializer.d.ts.map +1 -0
- package/dist/generators/style-serializer.js +129 -0
- package/dist/generators/style-serializer.js.map +1 -0
- package/dist/generators/to-elementor.d.ts +13 -0
- package/dist/generators/to-elementor.d.ts.map +1 -0
- package/dist/generators/to-elementor.js +150 -0
- package/dist/generators/to-elementor.js.map +1 -0
- package/dist/generators/to-html-css.d.ts +22 -0
- package/dist/generators/to-html-css.d.ts.map +1 -0
- package/dist/generators/to-html-css.js +127 -0
- package/dist/generators/to-html-css.js.map +1 -0
- package/dist/generators/to-react.d.ts +22 -0
- package/dist/generators/to-react.d.ts.map +1 -0
- package/dist/generators/to-react.js +123 -0
- package/dist/generators/to-react.js.map +1 -0
- package/dist/generators/to-svelte.d.ts +15 -0
- package/dist/generators/to-svelte.d.ts.map +1 -0
- package/dist/generators/to-svelte.js +105 -0
- package/dist/generators/to-svelte.js.map +1 -0
- package/dist/generators/to-vue.d.ts +17 -0
- package/dist/generators/to-vue.d.ts.map +1 -0
- package/dist/generators/to-vue.js +108 -0
- package/dist/generators/to-vue.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +228 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/input.schema.d.ts +245 -0
- package/dist/schemas/input.schema.d.ts.map +1 -0
- package/dist/schemas/input.schema.js +66 -0
- package/dist/schemas/input.schema.js.map +1 -0
- package/dist/schemas/vir.schema.d.ts +1786 -0
- package/dist/schemas/vir.schema.d.ts.map +1 -0
- package/dist/schemas/vir.schema.js +201 -0
- package/dist/schemas/vir.schema.js.map +1 -0
- package/dist/tools/convert-design.d.ts +40 -0
- package/dist/tools/convert-design.d.ts.map +1 -0
- package/dist/tools/convert-design.js +106 -0
- package/dist/tools/convert-design.js.map +1 -0
- package/dist/tools/validate-vir.d.ts +18 -0
- package/dist/tools/validate-vir.d.ts.map +1 -0
- package/dist/tools/validate-vir.js +32 -0
- package/dist/tools/validate-vir.js.map +1 -0
- package/dist/tools/wp-connector.d.ts +55 -0
- package/dist/tools/wp-connector.d.ts.map +1 -0
- package/dist/tools/wp-connector.js +146 -0
- package/dist/tools/wp-connector.js.map +1 -0
- package/dist/tools/wp-tools.d.ts +51 -0
- package/dist/tools/wp-tools.d.ts.map +1 -0
- package/dist/tools/wp-tools.js +126 -0
- package/dist/tools/wp-tools.js.map +1 -0
- package/dist/utils/errors.d.ts +35 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +67 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/id-generator.d.ts +27 -0
- package/dist/utils/id-generator.d.ts.map +1 -0
- package/dist/utils/id-generator.js +43 -0
- package/dist/utils/id-generator.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +19 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/metrics.d.ts +30 -0
- package/dist/utils/metrics.d.ts.map +1 -0
- package/dist/utils/metrics.js +50 -0
- package/dist/utils/metrics.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +25 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +41 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/url-validator.d.ts +14 -0
- package/dist/utils/url-validator.d.ts.map +1 -0
- package/dist/utils/url-validator.js +37 -0
- package/dist/utils/url-validator.js.map +1 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# MUCV — Motor Universal de Conversión Visual
|
|
2
|
+
|
|
3
|
+
**Versión:** 0.5.0
|
|
4
|
+
**Protocolo:** MCP (Model Context Protocol)
|
|
5
|
+
**Runtime:** Node.js 20+
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Qué es MUCV
|
|
10
|
+
|
|
11
|
+
MUCV es un servidor MCP que convierte diseños entre formatos visuales usando una representación intermedia neutral (VIR):
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
SOURCE → Parser → Normalizer → VIR → Optimizer → Generator → TARGET
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Nunca hay conversión directa entre formatos. Todo pasa por el VIR.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Formatos Soportados
|
|
22
|
+
|
|
23
|
+
| Source ↓ \ Target → | Elementor | HTML+CSS | React | Vue | Svelte |
|
|
24
|
+
|----------------------|:---------:|:--------:|:-----:|:---:|:------:|
|
|
25
|
+
| Elementor | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
26
|
+
| HTML | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
27
|
+
| React (JSX) | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Instalación
|
|
32
|
+
|
|
33
|
+
### Como servidor MCP (recomendado)
|
|
34
|
+
|
|
35
|
+
Configura en tu cliente MCP (Claude Desktop, VS Code, etc.):
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mucv": {
|
|
40
|
+
"command": "npx",
|
|
41
|
+
"args": ["-y", "mucv@latest"],
|
|
42
|
+
"env": {
|
|
43
|
+
"WP_URL": "https://tu-sitio.com",
|
|
44
|
+
"WP_USER": "admin",
|
|
45
|
+
"WP_APP_PASSWORD": "xxxx xxxx xxxx xxxx xxxx xxxx"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> Las variables `WP_*` son opcionales. Solo se requieren para las herramientas de WordPress.
|
|
52
|
+
|
|
53
|
+
### Ejecución directa
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npx mucv
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Herramientas MCP (7 tools)
|
|
62
|
+
|
|
63
|
+
| Tool | Descripción | Requiere WP |
|
|
64
|
+
|------|-------------|:-----------:|
|
|
65
|
+
| `convert_design` | Convierte entre formatos visuales | No |
|
|
66
|
+
| `validate_vir` | Valida un VIR contra el schema Zod | No |
|
|
67
|
+
| `get_page_design` | Obtiene diseño de una página WP | Sí |
|
|
68
|
+
| `publish_design` | Publica un diseño en WP | Sí |
|
|
69
|
+
| `list_pages` | Lista páginas/posts de WP | Sí |
|
|
70
|
+
| `health_check` | Estado del servidor | No |
|
|
71
|
+
| `get_metrics` | Métricas de conversión | No |
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Seguridad
|
|
76
|
+
|
|
77
|
+
- Sanitización HTML completa (script, iframe, SVG, data: URIs, CSS injection)
|
|
78
|
+
- Validación de URLs en VIR (bloquea javascript:, data:, vbscript:)
|
|
79
|
+
- Rate limiting: 60 req/min (ventana deslizante)
|
|
80
|
+
- Límites de tamaño: content 5MB, title 500 chars, search 200 chars
|
|
81
|
+
- TypeScript strict, validación Zod en runtime
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Desarrollo Local
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
git clone https://github.com/yosnap/mucv.git
|
|
89
|
+
cd mucv
|
|
90
|
+
npm install
|
|
91
|
+
npm run build
|
|
92
|
+
npm test
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Scripts
|
|
96
|
+
|
|
97
|
+
| Script | Descripción |
|
|
98
|
+
|--------|-------------|
|
|
99
|
+
| `npm run build` | Compilar TypeScript |
|
|
100
|
+
| `npm test` | Ejecutar tests |
|
|
101
|
+
| `npm run test:coverage` | Tests con cobertura |
|
|
102
|
+
| `npm run test:e2e` | Tests E2E contra WordPress Docker |
|
|
103
|
+
| `npm run lint` | ESLint |
|
|
104
|
+
|
|
105
|
+
### Docker (solo para desarrollo)
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
docker compose up -d # WordPress + MySQL para testing
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Documentación
|
|
114
|
+
|
|
115
|
+
- [Arquitectura](./docs/ARCHITECTURE.md)
|
|
116
|
+
- [Especificación VIR](./docs/VIR-SPEC.md)
|
|
117
|
+
- [API Reference](./docs/API-REFERENCE.md)
|
|
118
|
+
- [Seguridad](./docs/SECURITY.md)
|
|
119
|
+
- [Roadmap](./docs/ROADMAP.md)
|
|
120
|
+
- [Changelog](./docs/CHANGELOG.md)
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Licencia
|
|
125
|
+
|
|
126
|
+
Por definir — uso interno hasta v1.0.0.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MUCV — Elementor Parser
|
|
3
|
+
* Convierte Elementor JSON → VisualIR
|
|
4
|
+
*
|
|
5
|
+
* Soporta:
|
|
6
|
+
* - Modelo clásico: section > column > widget
|
|
7
|
+
* - Modelo moderno (v3.6+): container > container > widget
|
|
8
|
+
*
|
|
9
|
+
* @see docs/VIR-SPEC.md
|
|
10
|
+
* @see .claude/agents/parser-dev.md
|
|
11
|
+
*/
|
|
12
|
+
import type { ParseResult } from './parse-result.js';
|
|
13
|
+
export declare const elementorParser: {
|
|
14
|
+
/**
|
|
15
|
+
* Parsea Elementor JSON y produce un VisualIR validado.
|
|
16
|
+
* @param raw — JSON string de Elementor (array de elementos o objeto con `elements`)
|
|
17
|
+
* @param sourceVersion — versión de Elementor (opcional, ej. "3.21")
|
|
18
|
+
* @param sourceId — ID del post de WordPress (opcional)
|
|
19
|
+
*/
|
|
20
|
+
parse(raw: string, sourceVersion?: string, sourceId?: string): ParseResult;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=elementor-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementor-parser.d.ts","sourceRoot":"","sources":["../../src/core/elementor-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAwBpD,eAAO,MAAM,eAAe;IAC1B;;;;;OAKG;eACQ,MAAM,kBAAkB,MAAM,aAAa,MAAM,GAAG,WAAW;CAgC3E,CAAA"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MUCV — Elementor Parser
|
|
3
|
+
* Convierte Elementor JSON → VisualIR
|
|
4
|
+
*
|
|
5
|
+
* Soporta:
|
|
6
|
+
* - Modelo clásico: section > column > widget
|
|
7
|
+
* - Modelo moderno (v3.6+): container > container > widget
|
|
8
|
+
*
|
|
9
|
+
* @see docs/VIR-SPEC.md
|
|
10
|
+
* @see .claude/agents/parser-dev.md
|
|
11
|
+
*/
|
|
12
|
+
import { createHash } from 'node:crypto';
|
|
13
|
+
import { VisualIRSchema } from '../schemas/vir.schema.js';
|
|
14
|
+
import { mapElementorType, HTML_HEADING_LEVEL_MAP } from './mapper.js';
|
|
15
|
+
import { buildStyleFromElementor, normalizeText, normalizeSizeValue, normalizeColor, } from './normalizer.js';
|
|
16
|
+
import { logger } from '../utils/logger.js';
|
|
17
|
+
import { ParseError, VIRValidationError } from '../utils/errors.js';
|
|
18
|
+
// ─── Parser principal ─────────────────────────────────────────────────────────
|
|
19
|
+
export const elementorParser = {
|
|
20
|
+
/**
|
|
21
|
+
* Parsea Elementor JSON y produce un VisualIR validado.
|
|
22
|
+
* @param raw — JSON string de Elementor (array de elementos o objeto con `elements`)
|
|
23
|
+
* @param sourceVersion — versión de Elementor (opcional, ej. "3.21")
|
|
24
|
+
* @param sourceId — ID del post de WordPress (opcional)
|
|
25
|
+
*/
|
|
26
|
+
parse(raw, sourceVersion, sourceId) {
|
|
27
|
+
let parsed;
|
|
28
|
+
try {
|
|
29
|
+
parsed = JSON.parse(raw);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
throw new ParseError('Elementor JSON inválido', { raw: raw.slice(0, 100) });
|
|
33
|
+
}
|
|
34
|
+
const elements = extractElements(parsed);
|
|
35
|
+
const root = elements.map((el, i) => parseElement(el, i));
|
|
36
|
+
const virId = generateVIRId({ elements, sourceId });
|
|
37
|
+
const vir = {
|
|
38
|
+
virVersion: '1.0.0',
|
|
39
|
+
id: virId,
|
|
40
|
+
source: {
|
|
41
|
+
type: 'elementor',
|
|
42
|
+
...(sourceVersion !== undefined && { version: sourceVersion }),
|
|
43
|
+
...(sourceId !== undefined && { sourceId }),
|
|
44
|
+
},
|
|
45
|
+
root,
|
|
46
|
+
createdAt: new Date().toISOString(),
|
|
47
|
+
};
|
|
48
|
+
const result = VisualIRSchema.safeParse(vir);
|
|
49
|
+
if (!result.success) {
|
|
50
|
+
throw new VIRValidationError(result.error, { sourceType: 'elementor' });
|
|
51
|
+
}
|
|
52
|
+
logger.debug({ nodeCount: root.length, sourceId }, 'Elementor VIR parsed');
|
|
53
|
+
return { vir: result.data, warnings: [] };
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
// ─── Extracción de elementos ──────────────────────────────────────────────────
|
|
57
|
+
function extractElements(parsed) {
|
|
58
|
+
if (Array.isArray(parsed))
|
|
59
|
+
return parsed;
|
|
60
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
61
|
+
const obj = parsed;
|
|
62
|
+
// Elemento único con elType: lo envolvemos (puede tener su propio campo "elements" de hijos)
|
|
63
|
+
if (typeof obj['elType'] === 'string')
|
|
64
|
+
return [parsed];
|
|
65
|
+
// Wrapper sin elType pero con array de elementos raíz
|
|
66
|
+
if (Array.isArray(obj['elements']))
|
|
67
|
+
return obj['elements'];
|
|
68
|
+
}
|
|
69
|
+
throw new ParseError('Estructura Elementor no reconocida', {
|
|
70
|
+
type: typeof parsed,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// ─── Parseo recursivo de elementos ───────────────────────────────────────────
|
|
74
|
+
function parseElement(el, index) {
|
|
75
|
+
const type = mapElementorType(el.elType, el.widgetType);
|
|
76
|
+
const nodeId = generateNodeId(el.id ?? `${el.elType}-${index}`, index);
|
|
77
|
+
const styles = buildStyleFromElementor(el.settings);
|
|
78
|
+
const content = extractContent(el, type);
|
|
79
|
+
const children = el.elements?.map((child, i) => parseElement(child, i));
|
|
80
|
+
const meta = {
|
|
81
|
+
...(el.id !== undefined && { originalId: el.id }),
|
|
82
|
+
originalType: el.widgetType ?? el.elType,
|
|
83
|
+
elementorSettings: el.settings,
|
|
84
|
+
};
|
|
85
|
+
const node = {
|
|
86
|
+
id: nodeId,
|
|
87
|
+
type,
|
|
88
|
+
...(styles !== undefined && { styles }),
|
|
89
|
+
...(content !== undefined && { content }),
|
|
90
|
+
...(children !== undefined && children.length > 0 && { children }),
|
|
91
|
+
meta,
|
|
92
|
+
};
|
|
93
|
+
return node;
|
|
94
|
+
}
|
|
95
|
+
// ─── Extracción de contenido por tipo ────────────────────────────────────────
|
|
96
|
+
function extractContent(el, type) {
|
|
97
|
+
const s = el.settings;
|
|
98
|
+
switch (type) {
|
|
99
|
+
case 'heading': {
|
|
100
|
+
const text = normalizeText(s['title']);
|
|
101
|
+
const headerSize = typeof s['header_size'] === 'string' ? s['header_size'] : 'h2';
|
|
102
|
+
const level = HTML_HEADING_LEVEL_MAP[headerSize.toLowerCase()] ?? 2;
|
|
103
|
+
return text !== undefined ? { text, level } : { level };
|
|
104
|
+
}
|
|
105
|
+
case 'text': {
|
|
106
|
+
const richText = normalizeText(s['editor'] ?? s['text']);
|
|
107
|
+
return richText !== undefined ? { richText } : undefined;
|
|
108
|
+
}
|
|
109
|
+
case 'image': {
|
|
110
|
+
const imgData = s['image'];
|
|
111
|
+
const src = typeof imgData?.['url'] === 'string' ? imgData['url'] : undefined;
|
|
112
|
+
const alt = normalizeText(s['image_alt'] ?? imgData?.['alt']);
|
|
113
|
+
const width = typeof s['image_custom_dimension'] === 'object'
|
|
114
|
+
? s['image_custom_dimension']['width']
|
|
115
|
+
: undefined;
|
|
116
|
+
const height = typeof s['image_custom_dimension'] === 'object'
|
|
117
|
+
? s['image_custom_dimension']['height']
|
|
118
|
+
: undefined;
|
|
119
|
+
if (!src)
|
|
120
|
+
return undefined;
|
|
121
|
+
return {
|
|
122
|
+
src,
|
|
123
|
+
...(alt !== undefined && { alt }),
|
|
124
|
+
...(typeof width === 'number' && { width }),
|
|
125
|
+
...(typeof height === 'number' && { height }),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
case 'button': {
|
|
129
|
+
const label = normalizeText(s['text'] ?? s['button_text']);
|
|
130
|
+
const href = normalizeText(typeof s['link'] === 'object' && s['link'] !== null
|
|
131
|
+
? s['link']['url']
|
|
132
|
+
: s['url']);
|
|
133
|
+
const targetRaw = typeof s['link'] === 'object' && s['link'] !== null
|
|
134
|
+
? s['link']['is_external']
|
|
135
|
+
: undefined;
|
|
136
|
+
const target = targetRaw === true || targetRaw === 'yes' ? '_blank' : '_self';
|
|
137
|
+
return {
|
|
138
|
+
...(label !== undefined && { label }),
|
|
139
|
+
...(href !== undefined && { href }),
|
|
140
|
+
target,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
case 'list': {
|
|
144
|
+
const rawItems = s['icon_list'] ?? s['items'];
|
|
145
|
+
if (!Array.isArray(rawItems))
|
|
146
|
+
return undefined;
|
|
147
|
+
const items = rawItems
|
|
148
|
+
.filter((item) => typeof item === 'object' && item !== null)
|
|
149
|
+
.map((item, i) => ({
|
|
150
|
+
id: generateNodeId(`list-item-${i}`, i),
|
|
151
|
+
text: normalizeText(item['text'] ?? item['title']) ?? '',
|
|
152
|
+
...(typeof item['link'] === 'object' && item['link'] !== null
|
|
153
|
+
? { href: normalizeText(item['link']['url']) }
|
|
154
|
+
: {}),
|
|
155
|
+
}));
|
|
156
|
+
return items.length > 0 ? { items } : undefined;
|
|
157
|
+
}
|
|
158
|
+
case 'custom': {
|
|
159
|
+
const size = normalizeSizeValue(s['width']);
|
|
160
|
+
const color = normalizeColor(s['color']);
|
|
161
|
+
return {
|
|
162
|
+
rawData: s,
|
|
163
|
+
...(size !== undefined && { text: size }),
|
|
164
|
+
...(color !== undefined && {}),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
default:
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// ─── Generación de IDs ────────────────────────────────────────────────────────
|
|
172
|
+
function generateNodeId(originalId, index) {
|
|
173
|
+
const hash = createHash('sha256')
|
|
174
|
+
.update(`${originalId}-${index}`)
|
|
175
|
+
.digest('hex')
|
|
176
|
+
.slice(0, 8);
|
|
177
|
+
return `node_${hash}`;
|
|
178
|
+
}
|
|
179
|
+
function generateVIRId(content) {
|
|
180
|
+
const hash = createHash('sha256')
|
|
181
|
+
.update(JSON.stringify(content))
|
|
182
|
+
.digest('hex')
|
|
183
|
+
.slice(0, 8);
|
|
184
|
+
return `vir_${hash}`;
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=elementor-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementor-parser.js","sourceRoot":"","sources":["../../src/core/elementor-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MUCV — HTML Parser
|
|
3
|
+
* Convierte un string HTML → VisualIR
|
|
4
|
+
*
|
|
5
|
+
* Seguridad: elimina <script>, <iframe>, atributos on* y javascript: URLs
|
|
6
|
+
* antes de procesar. Cada elemento eliminado genera un warning en el log.
|
|
7
|
+
*
|
|
8
|
+
* @see docs/VIR-SPEC.md
|
|
9
|
+
* @see docs/SECURITY.md
|
|
10
|
+
* @see .claude/agents/parser-dev.md
|
|
11
|
+
*/
|
|
12
|
+
import type { ParseResult } from './parse-result.js';
|
|
13
|
+
export declare const htmlParser: {
|
|
14
|
+
/**
|
|
15
|
+
* Parsea un string HTML y produce un VisualIR validado.
|
|
16
|
+
* @param raw — string HTML completo o fragmento
|
|
17
|
+
* @param sourceId — identificador externo opcional
|
|
18
|
+
*/
|
|
19
|
+
parse(raw: string, sourceId?: string): ParseResult;
|
|
20
|
+
};
|
|
21
|
+
interface SanitizeResult {
|
|
22
|
+
html: string;
|
|
23
|
+
warnings: string[];
|
|
24
|
+
}
|
|
25
|
+
declare function sanitizeHtml(raw: string): SanitizeResult;
|
|
26
|
+
export { sanitizeHtml as _sanitizeHtml };
|
|
27
|
+
//# sourceMappingURL=html-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-parser.d.ts","sourceRoot":"","sources":["../../src/core/html-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAmBpD,eAAO,MAAM,UAAU;IACrB;;;;OAIG;eACQ,MAAM,aAAa,MAAM,GAAG,WAAW;CA0CnD,CAAA;AAID,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,iBAAS,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CA6EjD;AAuJD,OAAO,EAAE,YAAY,IAAI,aAAa,EAAE,CAAA"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MUCV — HTML Parser
|
|
3
|
+
* Convierte un string HTML → VisualIR
|
|
4
|
+
*
|
|
5
|
+
* Seguridad: elimina <script>, <iframe>, atributos on* y javascript: URLs
|
|
6
|
+
* antes de procesar. Cada elemento eliminado genera un warning en el log.
|
|
7
|
+
*
|
|
8
|
+
* @see docs/VIR-SPEC.md
|
|
9
|
+
* @see docs/SECURITY.md
|
|
10
|
+
* @see .claude/agents/parser-dev.md
|
|
11
|
+
*/
|
|
12
|
+
import { createHash } from 'node:crypto';
|
|
13
|
+
import { parse as parseHTML } from 'node-html-parser';
|
|
14
|
+
import { VisualIRSchema } from '../schemas/vir.schema.js';
|
|
15
|
+
import { mapHtmlTag, HTML_HEADING_LEVEL_MAP, } from './mapper.js';
|
|
16
|
+
import { buildStyleFromHtml, normalizeText } from './normalizer.js';
|
|
17
|
+
import { logger } from '../utils/logger.js';
|
|
18
|
+
import { ParseError, VIRValidationError } from '../utils/errors.js';
|
|
19
|
+
// ─── Etiquetas y atributos peligrosos ─────────────────────────────────────────
|
|
20
|
+
const DANGEROUS_TAGS = new Set(['script', 'iframe', 'object', 'embed', 'form', 'input', 'meta', 'link', 'svg']);
|
|
21
|
+
const DANGEROUS_ATTR_PATTERN = /^on[a-z]/i;
|
|
22
|
+
const SKIP_TAGS = new Set(['style', 'head', 'html', 'body', 'noscript', 'template']);
|
|
23
|
+
const INLINE_TAGS = new Set(['strong', 'em', 'b', 'i', 'u', 's', 'code', 'abbr', 'small', 'mark']);
|
|
24
|
+
// ─── Parser principal ─────────────────────────────────────────────────────────
|
|
25
|
+
export const htmlParser = {
|
|
26
|
+
/**
|
|
27
|
+
* Parsea un string HTML y produce un VisualIR validado.
|
|
28
|
+
* @param raw — string HTML completo o fragmento
|
|
29
|
+
* @param sourceId — identificador externo opcional
|
|
30
|
+
*/
|
|
31
|
+
parse(raw, sourceId) {
|
|
32
|
+
if (typeof raw !== 'string') {
|
|
33
|
+
throw new ParseError('HTML input debe ser un string', { type: typeof raw });
|
|
34
|
+
}
|
|
35
|
+
const sanitizeResult = sanitizeHtml(raw);
|
|
36
|
+
const dom = parseHTML(sanitizeResult.html, { comment: false, blockTextElements: { script: false } });
|
|
37
|
+
// Tomar el body si existe, o el root
|
|
38
|
+
const rootEl = dom.querySelector('body') ?? dom;
|
|
39
|
+
const children = rootEl.childNodes
|
|
40
|
+
.filter((n) => n.nodeType === 1)
|
|
41
|
+
.map((el, i) => parseElement(el, i))
|
|
42
|
+
.filter((n) => n !== null);
|
|
43
|
+
if (children.length === 0) {
|
|
44
|
+
throw new ParseError('HTML no contiene elementos parseables', {
|
|
45
|
+
raw: raw.slice(0, 100),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const virId = generateVIRId({ raw: raw.slice(0, 200), sourceId });
|
|
49
|
+
const vir = {
|
|
50
|
+
virVersion: '1.0.0',
|
|
51
|
+
id: virId,
|
|
52
|
+
source: {
|
|
53
|
+
type: 'html',
|
|
54
|
+
...(sourceId !== undefined && { sourceId }),
|
|
55
|
+
},
|
|
56
|
+
root: children,
|
|
57
|
+
createdAt: new Date().toISOString(),
|
|
58
|
+
};
|
|
59
|
+
const result = VisualIRSchema.safeParse(vir);
|
|
60
|
+
if (!result.success) {
|
|
61
|
+
throw new VIRValidationError(result.error, { sourceType: 'html' });
|
|
62
|
+
}
|
|
63
|
+
logger.debug({ nodeCount: children.length, sourceId }, 'HTML VIR parsed');
|
|
64
|
+
return { vir: result.data, warnings: sanitizeResult.warnings };
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
function sanitizeHtml(raw) {
|
|
68
|
+
const warnings = [];
|
|
69
|
+
let sanitized = raw;
|
|
70
|
+
// 1. Eliminar bloques completos de etiquetas peligrosas
|
|
71
|
+
for (const tag of DANGEROUS_TAGS) {
|
|
72
|
+
const pattern = new RegExp(`<${tag}[\\s\\S]*?<\\/${tag}>`, 'gi');
|
|
73
|
+
const selfClose = new RegExp(`<${tag}[^>]*\\/?>`, 'gi');
|
|
74
|
+
const before = sanitized;
|
|
75
|
+
sanitized = sanitized.replace(pattern, '').replace(selfClose, '');
|
|
76
|
+
if (sanitized !== before) {
|
|
77
|
+
const msg = `Unsafe HTML element <${tag}> removed during sanitization`;
|
|
78
|
+
warnings.push(msg);
|
|
79
|
+
logger.warn({ tag }, msg);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// 2. Eliminar atributos on*
|
|
83
|
+
sanitized = sanitized.replace(/\s+on[a-z][a-z0-9]*\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]*)/gi, (match) => {
|
|
84
|
+
const msg = `Dangerous event attribute removed: ${match.trim().slice(0, 30)}`;
|
|
85
|
+
warnings.push(msg);
|
|
86
|
+
logger.warn({ attr: match.slice(0, 30) }, msg);
|
|
87
|
+
return '';
|
|
88
|
+
});
|
|
89
|
+
// 3. Eliminar href="javascript:" y href="vbscript:"
|
|
90
|
+
sanitized = sanitized.replace(/href\s*=\s*(?:"(?:javascript|vbscript):[^"]*"|'(?:javascript|vbscript):[^']*')/gi, (match) => {
|
|
91
|
+
const msg = `Dangerous URL scheme removed: ${match.slice(0, 30)}`;
|
|
92
|
+
warnings.push(msg);
|
|
93
|
+
logger.warn({ attr: match.slice(0, 30) }, msg);
|
|
94
|
+
return 'href="#"';
|
|
95
|
+
});
|
|
96
|
+
// 4. Eliminar src/href con data: URIs
|
|
97
|
+
sanitized = sanitized.replace(/(?:src|href)\s*=\s*(?:"data:[^"]*"|'data:[^']*')/gi, (match) => {
|
|
98
|
+
const msg = `data: URI removed: ${match.slice(0, 40)}`;
|
|
99
|
+
warnings.push(msg);
|
|
100
|
+
logger.warn({ attr: match.slice(0, 40) }, msg);
|
|
101
|
+
return '';
|
|
102
|
+
});
|
|
103
|
+
// 5. Eliminar expression() y url(javascript:) en atributos style
|
|
104
|
+
sanitized = sanitized.replace(/style\s*=\s*"[^"]*"/gi, (match) => {
|
|
105
|
+
if (/expression\s*\(/i.test(match) || /url\s*\(\s*(?:"?\s*javascript:|'?\s*javascript:)/i.test(match)) {
|
|
106
|
+
const msg = `Dangerous CSS expression removed from style attribute`;
|
|
107
|
+
warnings.push(msg);
|
|
108
|
+
logger.warn({ style: match.slice(0, 50) }, msg);
|
|
109
|
+
return '';
|
|
110
|
+
}
|
|
111
|
+
return match;
|
|
112
|
+
});
|
|
113
|
+
sanitized = sanitized.replace(/style\s*=\s*'[^']*'/gi, (match) => {
|
|
114
|
+
if (/expression\s*\(/i.test(match) || /url\s*\(\s*(?:"?\s*javascript:|'?\s*javascript:)/i.test(match)) {
|
|
115
|
+
const msg = `Dangerous CSS expression removed from style attribute`;
|
|
116
|
+
warnings.push(msg);
|
|
117
|
+
logger.warn({ style: match.slice(0, 50) }, msg);
|
|
118
|
+
return '';
|
|
119
|
+
}
|
|
120
|
+
return match;
|
|
121
|
+
});
|
|
122
|
+
return { html: sanitized, warnings };
|
|
123
|
+
}
|
|
124
|
+
// ─── Parseo recursivo ─────────────────────────────────────────────────────────
|
|
125
|
+
function parseElement(el, index) {
|
|
126
|
+
const tag = el.tagName?.toLowerCase() ?? '';
|
|
127
|
+
if (SKIP_TAGS.has(tag) || DANGEROUS_TAGS.has(tag))
|
|
128
|
+
return null;
|
|
129
|
+
if (INLINE_TAGS.has(tag))
|
|
130
|
+
return null; // inline elements son contenido, no nodos
|
|
131
|
+
// Detectar display en style inline para flex/grid
|
|
132
|
+
const inlineStyle = el.getAttribute('style') ?? undefined;
|
|
133
|
+
const cssDisplay = extractCssDisplay(inlineStyle);
|
|
134
|
+
const type = mapHtmlTag(tag, cssDisplay);
|
|
135
|
+
const nodeId = generateNodeId(`${tag}-${el.getAttribute('id') ?? ''}-${index}`, index);
|
|
136
|
+
const classNames = (el.getAttribute('class') ?? '').split(/\s+/).filter(Boolean);
|
|
137
|
+
const styles = buildStyleFromHtml(inlineStyle, classNames);
|
|
138
|
+
const content = extractContent(el, tag, type);
|
|
139
|
+
// Procesar children solo si el tipo tiene sentido como contenedor
|
|
140
|
+
const isLeaf = ['image', 'button'].includes(type) && type !== 'list';
|
|
141
|
+
const childNodes = isLeaf
|
|
142
|
+
? []
|
|
143
|
+
: el.childNodes
|
|
144
|
+
.filter((n) => n.nodeType === 1)
|
|
145
|
+
.map((child, i) => parseElement(child, i))
|
|
146
|
+
.filter((n) => n !== null);
|
|
147
|
+
const meta = {
|
|
148
|
+
...(el.getAttribute('id') !== null && { originalId: el.getAttribute('id') ?? undefined }),
|
|
149
|
+
originalType: tag,
|
|
150
|
+
...(classNames.length > 0 && { cssClasses: classNames }),
|
|
151
|
+
};
|
|
152
|
+
// Verificar atributos peligrosos residuales
|
|
153
|
+
for (const [attr] of Object.entries(el.attributes)) {
|
|
154
|
+
if (DANGEROUS_ATTR_PATTERN.test(attr)) {
|
|
155
|
+
logger.warn({ attr, tag }, 'Residual dangerous attribute found after sanitization');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const node = {
|
|
159
|
+
id: nodeId,
|
|
160
|
+
type,
|
|
161
|
+
...(styles !== undefined && { styles }),
|
|
162
|
+
...(content !== undefined && { content }),
|
|
163
|
+
...(childNodes.length > 0 && { children: childNodes }),
|
|
164
|
+
meta,
|
|
165
|
+
};
|
|
166
|
+
return node;
|
|
167
|
+
}
|
|
168
|
+
// ─── Extracción de contenido ──────────────────────────────────────────────────
|
|
169
|
+
function extractContent(el, tag, type) {
|
|
170
|
+
switch (type) {
|
|
171
|
+
case 'heading': {
|
|
172
|
+
const level = HTML_HEADING_LEVEL_MAP[tag] ?? 2;
|
|
173
|
+
const text = normalizeText(el.textContent);
|
|
174
|
+
return { level, ...(text !== undefined && { text }) };
|
|
175
|
+
}
|
|
176
|
+
case 'text': {
|
|
177
|
+
const text = normalizeText(el.textContent);
|
|
178
|
+
const innerHtml = el.innerHTML?.trim();
|
|
179
|
+
if (innerHtml && innerHtml !== text) {
|
|
180
|
+
return { richText: innerHtml };
|
|
181
|
+
}
|
|
182
|
+
return text !== undefined ? { text } : undefined;
|
|
183
|
+
}
|
|
184
|
+
case 'image': {
|
|
185
|
+
const src = el.getAttribute('src') ?? el.querySelector('img')?.getAttribute('src');
|
|
186
|
+
const alt = el.getAttribute('alt') ?? el.querySelector('img')?.getAttribute('alt');
|
|
187
|
+
if (!src)
|
|
188
|
+
return undefined;
|
|
189
|
+
return {
|
|
190
|
+
src,
|
|
191
|
+
...(alt !== undefined && alt !== null && { alt }),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
case 'button': {
|
|
195
|
+
const href = el.getAttribute('href');
|
|
196
|
+
const targetAttr = el.getAttribute('target');
|
|
197
|
+
const label = normalizeText(el.textContent);
|
|
198
|
+
const target = targetAttr === '_blank' ? '_blank' : '_self';
|
|
199
|
+
return {
|
|
200
|
+
...(label !== undefined && { label }),
|
|
201
|
+
...(href !== null && href !== undefined && href !== '#' && { href }),
|
|
202
|
+
target,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
case 'list': {
|
|
206
|
+
const isOrdered = tag === 'ol';
|
|
207
|
+
const listItems = el.querySelectorAll('li');
|
|
208
|
+
const items = listItems.map((li, i) => ({
|
|
209
|
+
id: generateNodeId(`li-${i}`, i),
|
|
210
|
+
text: normalizeText(li.textContent) ?? '',
|
|
211
|
+
...(li.querySelector('a')?.getAttribute('href')
|
|
212
|
+
? { href: li.querySelector('a')?.getAttribute('href') ?? undefined }
|
|
213
|
+
: {}),
|
|
214
|
+
}));
|
|
215
|
+
return items.length > 0
|
|
216
|
+
? { items, ...(isOrdered && { rawData: { ordered: true } }) }
|
|
217
|
+
: undefined;
|
|
218
|
+
}
|
|
219
|
+
default:
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// ─── Helpers de CSS ───────────────────────────────────────────────────────────
|
|
224
|
+
function extractCssDisplay(inlineStyle) {
|
|
225
|
+
if (!inlineStyle)
|
|
226
|
+
return undefined;
|
|
227
|
+
const match = inlineStyle.match(/display\s*:\s*([^;]+)/i);
|
|
228
|
+
return match?.[1]?.trim().toLowerCase();
|
|
229
|
+
}
|
|
230
|
+
// ─── Generación de IDs ────────────────────────────────────────────────────────
|
|
231
|
+
function generateNodeId(seed, index) {
|
|
232
|
+
const hash = createHash('sha256')
|
|
233
|
+
.update(`${seed}-${index}`)
|
|
234
|
+
.digest('hex')
|
|
235
|
+
.slice(0, 8);
|
|
236
|
+
return `node_${hash}`;
|
|
237
|
+
}
|
|
238
|
+
function generateVIRId(content) {
|
|
239
|
+
const hash = createHash('sha256')
|
|
240
|
+
.update(JSON.stringify(content))
|
|
241
|
+
.digest('hex')
|
|
242
|
+
.slice(0, 8);
|
|
243
|
+
return `vir_${hash}`;
|
|
244
|
+
}
|
|
245
|
+
// ─── Exports adicionales para testing ────────────────────────────────────────
|
|
246
|
+
export { sanitizeHtml as _sanitizeHtml };
|
|
247
|
+
//# sourceMappingURL=html-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-parser.js","sourceRoot":"","sources":["../../src/core/html-parser.ts"],"names":[],"mappings":"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"}
|