@ssolis-ti/mcp-compra-agil 1.0.3 → 2.3.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 +111 -23
- package/dist/api/compra-agil-client.d.ts +37 -2
- package/dist/api/compra-agil-client.d.ts.map +1 -1
- package/dist/api/compra-agil-client.js +76 -5
- package/dist/api/compra-agil-client.js.map +1 -1
- package/dist/index.js +32 -8
- package/dist/index.js.map +1 -1
- package/dist/reports/components.d.ts +59 -0
- package/dist/reports/components.d.ts.map +1 -0
- package/dist/reports/components.js +125 -0
- package/dist/reports/components.js.map +1 -0
- package/dist/reports/export.d.ts +27 -0
- package/dist/reports/export.d.ts.map +1 -0
- package/dist/reports/export.js +47 -0
- package/dist/reports/export.js.map +1 -0
- package/dist/reports/format.d.ts +24 -0
- package/dist/reports/format.d.ts.map +1 -0
- package/dist/reports/format.js +74 -0
- package/dist/reports/format.js.map +1 -0
- package/dist/reports/render.d.ts +18 -0
- package/dist/reports/render.d.ts.map +1 -0
- package/dist/reports/render.js +30 -0
- package/dist/reports/render.js.map +1 -0
- package/dist/reports/templates/radar-oportunidades.d.ts +41 -0
- package/dist/reports/templates/radar-oportunidades.d.ts.map +1 -0
- package/dist/reports/templates/radar-oportunidades.js +170 -0
- package/dist/reports/templates/radar-oportunidades.js.map +1 -0
- package/dist/reports/theme.d.ts +83 -0
- package/dist/reports/theme.d.ts.map +1 -0
- package/dist/reports/theme.js +284 -0
- package/dist/reports/theme.js.map +1 -0
- package/dist/resources/compras-template.d.ts.map +1 -1
- package/dist/resources/compras-template.js +2 -1
- package/dist/resources/compras-template.js.map +1 -1
- package/dist/resources/documentacion.d.ts.map +1 -1
- package/dist/resources/documentacion.js +5 -36
- package/dist/resources/documentacion.js.map +1 -1
- package/dist/resources/estados.d.ts.map +1 -1
- package/dist/resources/estados.js +24 -10
- package/dist/resources/estados.js.map +1 -1
- package/dist/services/monitor.js +38 -3
- package/dist/services/monitor.js.map +1 -1
- package/dist/tools/analizar-precios-mercado.d.ts +21 -0
- package/dist/tools/analizar-precios-mercado.d.ts.map +1 -0
- package/dist/tools/analizar-precios-mercado.js +217 -0
- package/dist/tools/analizar-precios-mercado.js.map +1 -0
- package/dist/tools/auditar-desiertas.d.ts.map +1 -1
- package/dist/tools/auditar-desiertas.js +66 -48
- package/dist/tools/auditar-desiertas.js.map +1 -1
- package/dist/tools/buscar-compras.d.ts.map +1 -1
- package/dist/tools/buscar-compras.js +2 -1
- package/dist/tools/buscar-compras.js.map +1 -1
- package/dist/tools/detalle-compra.d.ts.map +1 -1
- package/dist/tools/detalle-compra.js +6 -5
- package/dist/tools/detalle-compra.js.map +1 -1
- package/dist/tools/detalle-oc.js +6 -6
- package/dist/tools/detalle-oc.js.map +1 -1
- package/dist/tools/documentos.d.ts.map +1 -1
- package/dist/tools/documentos.js +75 -81
- package/dist/tools/documentos.js.map +1 -1
- package/dist/tools/estadisticas-uso.d.ts.map +1 -1
- package/dist/tools/estadisticas-uso.js +12 -2
- package/dist/tools/estadisticas-uso.js.map +1 -1
- package/dist/tools/generar-borrador.d.ts +25 -1
- package/dist/tools/generar-borrador.d.ts.map +1 -1
- package/dist/tools/generar-borrador.js +111 -82
- package/dist/tools/generar-borrador.js.map +1 -1
- package/dist/tools/generar-informe.d.ts +13 -0
- package/dist/tools/generar-informe.d.ts.map +1 -0
- package/dist/tools/generar-informe.js +111 -0
- package/dist/tools/generar-informe.js.map +1 -0
- package/dist/tools/monitorear-cambios.d.ts +32 -2
- package/dist/tools/monitorear-cambios.d.ts.map +1 -1
- package/dist/tools/monitorear-cambios.js +79 -10
- package/dist/tools/monitorear-cambios.js.map +1 -1
- package/dist/tools/radar-oportunidades.d.ts +42 -1
- package/dist/tools/radar-oportunidades.d.ts.map +1 -1
- package/dist/tools/radar-oportunidades.js +123 -103
- package/dist/tools/radar-oportunidades.js.map +1 -1
- package/dist/tools/recomendar-precio.d.ts +13 -0
- package/dist/tools/recomendar-precio.d.ts.map +1 -1
- package/dist/tools/recomendar-precio.js +53 -57
- package/dist/tools/recomendar-precio.js.map +1 -1
- package/dist/tools/verificar-oc.d.ts.map +1 -1
- package/dist/tools/verificar-oc.js +36 -8
- package/dist/tools/verificar-oc.js.map +1 -1
- package/dist/tools/verificar-ticket.d.ts +13 -0
- package/dist/tools/verificar-ticket.d.ts.map +1 -0
- package/dist/tools/verificar-ticket.js +79 -0
- package/dist/tools/verificar-ticket.js.map +1 -0
- package/dist/utils/cache.d.ts +53 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +131 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/doc-search.d.ts +52 -0
- package/dist/utils/doc-search.d.ts.map +1 -0
- package/dist/utils/doc-search.js +97 -0
- package/dist/utils/doc-search.js.map +1 -0
- package/dist/utils/docs-locator.d.ts +41 -0
- package/dist/utils/docs-locator.d.ts.map +1 -0
- package/dist/utils/docs-locator.js +82 -0
- package/dist/utils/docs-locator.js.map +1 -0
- package/dist/utils/error-handler.d.ts.map +1 -1
- package/dist/utils/error-handler.js +8 -7
- package/dist/utils/error-handler.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +9 -6
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/quotation.d.ts +71 -0
- package/dist/utils/quotation.d.ts.map +1 -0
- package/dist/utils/quotation.js +140 -0
- package/dist/utils/quotation.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +44 -2
- package/dist/utils/rate-limiter.d.ts.map +1 -1
- package/dist/utils/rate-limiter.js +125 -13
- package/dist/utils/rate-limiter.js.map +1 -1
- package/dist/utils/redact.d.ts +48 -0
- package/dist/utils/redact.d.ts.map +1 -0
- package/dist/utils/redact.js +100 -0
- package/dist/utils/redact.js.map +1 -0
- package/package.json +55 -53
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Componentes de informe: funciones puras que emiten fragmentos de HTML.
|
|
3
|
+
*
|
|
4
|
+
* REGLA DE SEGURIDAD: todo dato que provenga de la API (nombres de organismos,
|
|
5
|
+
* razones sociales, descripciones) DEBE pasar por `esc()`. Un nombre con "&" o
|
|
6
|
+
* "<" rompería el documento — y en un HTML que el usuario abre en su navegador,
|
|
7
|
+
* inyectar marcado no escapado es una vulnerabilidad, no solo un bug visual.
|
|
8
|
+
*/
|
|
9
|
+
/** Escapa texto para interpolación segura en HTML. */
|
|
10
|
+
export declare function esc(valor: unknown): string;
|
|
11
|
+
export interface PortadaOpts {
|
|
12
|
+
eyebrow: string;
|
|
13
|
+
titulo: string;
|
|
14
|
+
subtitulo?: string;
|
|
15
|
+
meta?: Array<{
|
|
16
|
+
label: string;
|
|
17
|
+
valor: string;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
export declare function portada(o: PortadaOpts): string;
|
|
21
|
+
export interface Kpi {
|
|
22
|
+
label: string;
|
|
23
|
+
valor: string;
|
|
24
|
+
nota?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function kpiRow(kpis: Kpi[]): string;
|
|
27
|
+
export interface Columna<T> {
|
|
28
|
+
header: string;
|
|
29
|
+
/** Devuelve HTML ya escapado o seguro (usa `esc()` dentro si interpolas datos). */
|
|
30
|
+
celda: (fila: T, indice: number) => string;
|
|
31
|
+
/** Alinea a la derecha con cifras tabulares. */
|
|
32
|
+
numerica?: boolean;
|
|
33
|
+
ancho?: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function tabla<T>(filas: T[], columnas: Array<Columna<T>>): string;
|
|
36
|
+
export type TonoBadge = 'exito' | 'alerta' | 'critico' | 'neutro';
|
|
37
|
+
export declare function badge(texto: string, tono?: TonoBadge): string;
|
|
38
|
+
export declare function callout(titulo: string, cuerpoHtml: string, tono?: 'neutro' | 'alerta' | 'critico'): string;
|
|
39
|
+
export declare function lista(items: string[]): string;
|
|
40
|
+
export interface BarraDato {
|
|
41
|
+
etiqueta: string;
|
|
42
|
+
valor: number;
|
|
43
|
+
/** Texto mostrado al final de la barra. Por defecto, el valor crudo. */
|
|
44
|
+
valorTexto?: string;
|
|
45
|
+
color?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Gráfico de barras horizontal en SVG inline.
|
|
49
|
+
*
|
|
50
|
+
* SVG en vez de una librería de charts: es vectorial (imprime nítido a cualquier
|
|
51
|
+
* DPI), no requiere JS en tiempo de impresión y no añade dependencias.
|
|
52
|
+
*/
|
|
53
|
+
export declare function barChartSVG(datos: BarraDato[], opts?: {
|
|
54
|
+
ancho?: number;
|
|
55
|
+
alturaBarra?: number;
|
|
56
|
+
anchoEtiqueta?: number;
|
|
57
|
+
}): string;
|
|
58
|
+
export declare function pieDoc(texto: string): string;
|
|
59
|
+
//# sourceMappingURL=components.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/reports/components.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,sDAAsD;AACtD,wBAAgB,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAQ1C;AAID,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAW9C;AAID,MAAM,WAAW,GAAG;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,CAY1C;AAID,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAgBxE;AAID,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAElE,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,SAAoB,GAAG,MAAM,CAEvE;AAID,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,QAAQ,GAAG,QAAQ,GAAG,SAAoB,GAAG,MAAM,CAOpH;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAG7C;AAID,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,MAAM,CAuCnI;AAID,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5C"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Componentes de informe: funciones puras que emiten fragmentos de HTML.
|
|
3
|
+
*
|
|
4
|
+
* REGLA DE SEGURIDAD: todo dato que provenga de la API (nombres de organismos,
|
|
5
|
+
* razones sociales, descripciones) DEBE pasar por `esc()`. Un nombre con "&" o
|
|
6
|
+
* "<" rompería el documento — y en un HTML que el usuario abre en su navegador,
|
|
7
|
+
* inyectar marcado no escapado es una vulnerabilidad, no solo un bug visual.
|
|
8
|
+
*/
|
|
9
|
+
import { TOKENS } from './theme.js';
|
|
10
|
+
/** Escapa texto para interpolación segura en HTML. */
|
|
11
|
+
export function esc(valor) {
|
|
12
|
+
if (valor === null || valor === undefined)
|
|
13
|
+
return '';
|
|
14
|
+
return String(valor)
|
|
15
|
+
.replace(/&/g, '&')
|
|
16
|
+
.replace(/</g, '<')
|
|
17
|
+
.replace(/>/g, '>')
|
|
18
|
+
.replace(/"/g, '"')
|
|
19
|
+
.replace(/'/g, ''');
|
|
20
|
+
}
|
|
21
|
+
export function portada(o) {
|
|
22
|
+
const meta = (o.meta ?? [])
|
|
23
|
+
.map((m) => `<span>${esc(m.label)}: <b>${esc(m.valor)}</b></span>`)
|
|
24
|
+
.join('');
|
|
25
|
+
return `
|
|
26
|
+
<header class="portada">
|
|
27
|
+
<div class="eyebrow">${esc(o.eyebrow)}</div>
|
|
28
|
+
<h1 class="titulo">${esc(o.titulo)}</h1>
|
|
29
|
+
${o.subtitulo ? `<p class="subtitulo">${esc(o.subtitulo)}</p>` : ''}
|
|
30
|
+
${meta ? `<div class="meta">${meta}</div>` : ''}
|
|
31
|
+
</header>`.trim();
|
|
32
|
+
}
|
|
33
|
+
export function kpiRow(kpis) {
|
|
34
|
+
const items = kpis
|
|
35
|
+
.map((k) => `
|
|
36
|
+
<div class="kpi">
|
|
37
|
+
<div class="label">${esc(k.label)}</div>
|
|
38
|
+
<div class="valor">${esc(k.valor)}</div>
|
|
39
|
+
${k.nota ? `<div class="nota">${esc(k.nota)}</div>` : ''}
|
|
40
|
+
</div>`)
|
|
41
|
+
.join('');
|
|
42
|
+
return `<div class="kpi-row">${items}</div>`;
|
|
43
|
+
}
|
|
44
|
+
export function tabla(filas, columnas) {
|
|
45
|
+
if (filas.length === 0) {
|
|
46
|
+
return `<p><small>Sin datos para mostrar.</small></p>`;
|
|
47
|
+
}
|
|
48
|
+
const head = columnas
|
|
49
|
+
.map((c) => `<th${c.ancho ? ` style="width:${c.ancho}"` : ''}>${esc(c.header)}</th>`)
|
|
50
|
+
.join('');
|
|
51
|
+
const body = filas
|
|
52
|
+
.map((fila, i) => {
|
|
53
|
+
const celdas = columnas
|
|
54
|
+
.map((c) => `<td${c.numerica ? ' class="num"' : ''}>${c.celda(fila, i)}</td>`)
|
|
55
|
+
.join('');
|
|
56
|
+
return `<tr>${celdas}</tr>`;
|
|
57
|
+
})
|
|
58
|
+
.join('');
|
|
59
|
+
return `<table><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table>`;
|
|
60
|
+
}
|
|
61
|
+
export function badge(texto, tono = 'neutro') {
|
|
62
|
+
return `<span class="badge badge-${tono}">${esc(texto)}</span>`;
|
|
63
|
+
}
|
|
64
|
+
// ─── Callout ──────────────────────────────────────────────────────────
|
|
65
|
+
export function callout(titulo, cuerpoHtml, tono = 'neutro') {
|
|
66
|
+
const clase = tono === 'neutro' ? 'callout' : `callout callout-${tono}`;
|
|
67
|
+
return `
|
|
68
|
+
<div class="${clase}">
|
|
69
|
+
<div class="titulo">${esc(titulo)}</div>
|
|
70
|
+
<div>${cuerpoHtml}</div>
|
|
71
|
+
</div>`.trim();
|
|
72
|
+
}
|
|
73
|
+
export function lista(items) {
|
|
74
|
+
if (items.length === 0)
|
|
75
|
+
return '';
|
|
76
|
+
return `<ul style="margin:0;padding-left:4mm">${items.map((i) => `<li>${esc(i)}</li>`).join('')}</ul>`;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Gráfico de barras horizontal en SVG inline.
|
|
80
|
+
*
|
|
81
|
+
* SVG en vez de una librería de charts: es vectorial (imprime nítido a cualquier
|
|
82
|
+
* DPI), no requiere JS en tiempo de impresión y no añade dependencias.
|
|
83
|
+
*/
|
|
84
|
+
export function barChartSVG(datos, opts = {}) {
|
|
85
|
+
if (datos.length === 0)
|
|
86
|
+
return '';
|
|
87
|
+
const ancho = opts.ancho ?? 680;
|
|
88
|
+
const alturaBarra = opts.alturaBarra ?? 22;
|
|
89
|
+
const gap = 8;
|
|
90
|
+
const anchoEtiqueta = opts.anchoEtiqueta ?? 190;
|
|
91
|
+
const margenDerecho = 90;
|
|
92
|
+
const alto = datos.length * (alturaBarra + gap) + gap;
|
|
93
|
+
const anchoUtil = ancho - anchoEtiqueta - margenDerecho;
|
|
94
|
+
const maxValor = Math.max(...datos.map((d) => d.valor), 1);
|
|
95
|
+
const barras = datos
|
|
96
|
+
.map((d, i) => {
|
|
97
|
+
const y = gap + i * (alturaBarra + gap);
|
|
98
|
+
const w = Math.max(1, (d.valor / maxValor) * anchoUtil);
|
|
99
|
+
const color = d.color ?? TOKENS.color.marca;
|
|
100
|
+
const texto = d.valorTexto ?? String(d.valor);
|
|
101
|
+
// Etiqueta truncada para no romper el layout
|
|
102
|
+
const etiqueta = d.etiqueta.length > 30 ? `${d.etiqueta.slice(0, 29)}…` : d.etiqueta;
|
|
103
|
+
return `
|
|
104
|
+
<g>
|
|
105
|
+
<text x="${anchoEtiqueta - 6}" y="${y + alturaBarra / 2}" text-anchor="end" dominant-baseline="central"
|
|
106
|
+
font-size="10" fill="${TOKENS.color.tintaSuave}">${esc(etiqueta)}</text>
|
|
107
|
+
<rect x="${anchoEtiqueta}" y="${y}" width="${anchoUtil}" height="${alturaBarra}" fill="#f1f4f7" rx="2"/>
|
|
108
|
+
<rect x="${anchoEtiqueta}" y="${y}" width="${w.toFixed(1)}" height="${alturaBarra}" fill="${color}" rx="2"/>
|
|
109
|
+
<text x="${anchoEtiqueta + w + 6}" y="${y + alturaBarra / 2}" dominant-baseline="central"
|
|
110
|
+
font-size="10" font-weight="600" fill="${TOKENS.color.tinta}">${esc(texto)}</text>
|
|
111
|
+
</g>`;
|
|
112
|
+
})
|
|
113
|
+
.join('');
|
|
114
|
+
return `
|
|
115
|
+
<div class="chart">
|
|
116
|
+
<svg viewBox="0 0 ${ancho} ${alto}" role="img" xmlns="http://www.w3.org/2000/svg">
|
|
117
|
+
${barras}
|
|
118
|
+
</svg>
|
|
119
|
+
</div>`.trim();
|
|
120
|
+
}
|
|
121
|
+
// ─── Pie de documento ─────────────────────────────────────────────────
|
|
122
|
+
export function pieDoc(texto) {
|
|
123
|
+
return `<div class="pie-doc">${esc(texto)}</div>`;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=components.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components.js","sourceRoot":"","sources":["../../src/reports/components.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escritura de informes a disco.
|
|
3
|
+
*
|
|
4
|
+
* DECISIÓN CLAVE DE DISEÑO: el HTML NUNCA se devuelve al LLM.
|
|
5
|
+
* Un informe pesa decenas de KB; retornarlo como texto consumiría miles de
|
|
6
|
+
* tokens de contexto por cada llamada. La tool escribe el archivo y devuelve
|
|
7
|
+
* solo la ruta más un resumen breve.
|
|
8
|
+
*/
|
|
9
|
+
/** Directorio de salida por defecto: ./informes en el cwd del servidor. */
|
|
10
|
+
export declare function defaultOutputDir(): string;
|
|
11
|
+
/**
|
|
12
|
+
* Convierte un texto en un fragmento seguro para nombre de archivo.
|
|
13
|
+
* Ej: "Radar RM / julio" → "radar-rm-julio"
|
|
14
|
+
*/
|
|
15
|
+
export declare function slug(texto: string): string;
|
|
16
|
+
/** Marca temporal compacta para nombres de archivo: 20260715-0930 */
|
|
17
|
+
export declare function stamp(d?: Date): string;
|
|
18
|
+
export interface EscrituraResultado {
|
|
19
|
+
ruta: string;
|
|
20
|
+
bytes: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Escribe el HTML en disco creando el directorio si no existe.
|
|
24
|
+
* Retorna la ruta absoluta y el tamaño resultante.
|
|
25
|
+
*/
|
|
26
|
+
export declare function escribirInforme(html: string, nombreArchivo: string, dir?: string): EscrituraResultado;
|
|
27
|
+
//# sourceMappingURL=export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/reports/export.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,2EAA2E;AAC3E,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ1C;AAED,qEAAqE;AACrE,wBAAgB,KAAK,CAAC,CAAC,GAAE,IAAiB,GAAG,MAAM,CAGlD;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAUrG"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escritura de informes a disco.
|
|
3
|
+
*
|
|
4
|
+
* DECISIÓN CLAVE DE DISEÑO: el HTML NUNCA se devuelve al LLM.
|
|
5
|
+
* Un informe pesa decenas de KB; retornarlo como texto consumiría miles de
|
|
6
|
+
* tokens de contexto por cada llamada. La tool escribe el archivo y devuelve
|
|
7
|
+
* solo la ruta más un resumen breve.
|
|
8
|
+
*/
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { logger } from '../utils/logger.js';
|
|
12
|
+
/** Directorio de salida por defecto: ./informes en el cwd del servidor. */
|
|
13
|
+
export function defaultOutputDir() {
|
|
14
|
+
return path.resolve(process.cwd(), 'informes');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Convierte un texto en un fragmento seguro para nombre de archivo.
|
|
18
|
+
* Ej: "Radar RM / julio" → "radar-rm-julio"
|
|
19
|
+
*/
|
|
20
|
+
export function slug(texto) {
|
|
21
|
+
return texto
|
|
22
|
+
.normalize('NFD')
|
|
23
|
+
.replace(/[\u0300-\u036f]/g, '') // quitar diacríticos combinantes
|
|
24
|
+
.toLowerCase()
|
|
25
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
26
|
+
.replace(/^-+|-+$/g, '')
|
|
27
|
+
.slice(0, 60) || 'informe';
|
|
28
|
+
}
|
|
29
|
+
/** Marca temporal compacta para nombres de archivo: 20260715-0930 */
|
|
30
|
+
export function stamp(d = new Date()) {
|
|
31
|
+
const p = (n) => String(n).padStart(2, '0');
|
|
32
|
+
return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Escribe el HTML en disco creando el directorio si no existe.
|
|
36
|
+
* Retorna la ruta absoluta y el tamaño resultante.
|
|
37
|
+
*/
|
|
38
|
+
export function escribirInforme(html, nombreArchivo, dir) {
|
|
39
|
+
const destinoDir = dir ? path.resolve(dir) : defaultOutputDir();
|
|
40
|
+
fs.mkdirSync(destinoDir, { recursive: true });
|
|
41
|
+
const ruta = path.join(destinoDir, nombreArchivo);
|
|
42
|
+
fs.writeFileSync(ruta, html, 'utf8');
|
|
43
|
+
const bytes = Buffer.byteLength(html, 'utf8');
|
|
44
|
+
logger.info(`Informe generado: ${ruta} (${(bytes / 1024).toFixed(1)} KB)`);
|
|
45
|
+
return { ruta, bytes };
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/reports/export.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,2EAA2E;AAC3E,MAAM,UAAU,gBAAgB;IAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,IAAI,CAAC,KAAa;IAChC,OAAO,KAAK;SACT,SAAS,CAAC,KAAK,CAAC;SAChB,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,iCAAiC;SACjE,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC;AAC/B,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,KAAK,CAAC,IAAU,IAAI,IAAI,EAAE;IACxC,MAAM,CAAC,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,OAAO,GAAG,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC;AAC5G,CAAC;AAOD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,aAAqB,EAAE,GAAY;IAC/E,MAAM,UAAU,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAChE,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE9C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IAClD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAErC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9C,MAAM,CAAC,IAAI,CAAC,qBAAqB,IAAI,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC3E,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formateo localizado para informes (Chile).
|
|
3
|
+
*
|
|
4
|
+
* Fuente única de verdad para representar montos, fechas y RUTs.
|
|
5
|
+
* Evita que cada template invente su propio formato.
|
|
6
|
+
*/
|
|
7
|
+
/** Formatea un monto en pesos chilenos. Ej: 1250000 → "$1.250.000" */
|
|
8
|
+
export declare function clp(monto: number | null | undefined): string;
|
|
9
|
+
/** Formatea un número con separador de miles chileno. */
|
|
10
|
+
export declare function numero(valor: number | null | undefined, decimales?: number): string;
|
|
11
|
+
/** Formatea un porcentaje. Ej: 12.3 → "12,3%" */
|
|
12
|
+
export declare function porcentaje(valor: number | null | undefined, decimales?: number): string;
|
|
13
|
+
/** Formatea una fecha ISO a formato legible chileno. Ej: "15-07-2026 09:30" */
|
|
14
|
+
export declare function fecha(iso: string | null | undefined, conHora?: boolean): string;
|
|
15
|
+
/** Fecha larga para portadas. Ej: "15 de julio de 2026" */
|
|
16
|
+
export declare function fechaLarga(d?: Date): string;
|
|
17
|
+
/**
|
|
18
|
+
* Formatea un RUT chileno con puntos y guion. Ej: "761234567" → "76.123.456-7"
|
|
19
|
+
* Si el valor no parece un RUT, se devuelve tal cual.
|
|
20
|
+
*/
|
|
21
|
+
export declare function rut(valor: string | null | undefined): string;
|
|
22
|
+
/** Convierte horas a una glosa humana. Ej: 30.5 → "1d 6h" */
|
|
23
|
+
export declare function horasRestantes(horas: number | null | undefined): string;
|
|
24
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/reports/format.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,sEAAsE;AACtE,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAG5D;AAED,yDAAyD;AACzD,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,SAAS,SAAI,GAAG,MAAM,CAM9E;AAED,iDAAiD;AACjD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,SAAS,SAAI,GAAG,MAAM,CAGlF;AAED,+EAA+E;AAC/E,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,UAAO,GAAG,MAAM,CAW5E;AAED,2DAA2D;AAC3D,wBAAgB,UAAU,CAAC,CAAC,GAAE,IAAiB,GAAG,MAAM,CAEvD;AAED;;;GAGG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAO5D;AAED,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAOvE"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formateo localizado para informes (Chile).
|
|
3
|
+
*
|
|
4
|
+
* Fuente única de verdad para representar montos, fechas y RUTs.
|
|
5
|
+
* Evita que cada template invente su propio formato.
|
|
6
|
+
*/
|
|
7
|
+
/** Formatea un monto en pesos chilenos. Ej: 1250000 → "$1.250.000" */
|
|
8
|
+
export function clp(monto) {
|
|
9
|
+
if (monto === null || monto === undefined || !Number.isFinite(monto))
|
|
10
|
+
return '—';
|
|
11
|
+
return `$${Math.round(monto).toLocaleString('es-CL')}`;
|
|
12
|
+
}
|
|
13
|
+
/** Formatea un número con separador de miles chileno. */
|
|
14
|
+
export function numero(valor, decimales = 0) {
|
|
15
|
+
if (valor === null || valor === undefined || !Number.isFinite(valor))
|
|
16
|
+
return '—';
|
|
17
|
+
return valor.toLocaleString('es-CL', {
|
|
18
|
+
minimumFractionDigits: decimales,
|
|
19
|
+
maximumFractionDigits: decimales,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** Formatea un porcentaje. Ej: 12.3 → "12,3%" */
|
|
23
|
+
export function porcentaje(valor, decimales = 1) {
|
|
24
|
+
if (valor === null || valor === undefined || !Number.isFinite(valor))
|
|
25
|
+
return '—';
|
|
26
|
+
return `${numero(valor, decimales)}%`;
|
|
27
|
+
}
|
|
28
|
+
/** Formatea una fecha ISO a formato legible chileno. Ej: "15-07-2026 09:30" */
|
|
29
|
+
export function fecha(iso, conHora = true) {
|
|
30
|
+
if (!iso)
|
|
31
|
+
return '—';
|
|
32
|
+
const d = new Date(iso);
|
|
33
|
+
if (Number.isNaN(d.getTime()))
|
|
34
|
+
return '—';
|
|
35
|
+
const dia = String(d.getDate()).padStart(2, '0');
|
|
36
|
+
const mes = String(d.getMonth() + 1).padStart(2, '0');
|
|
37
|
+
const anio = d.getFullYear();
|
|
38
|
+
if (!conHora)
|
|
39
|
+
return `${dia}-${mes}-${anio}`;
|
|
40
|
+
const hh = String(d.getHours()).padStart(2, '0');
|
|
41
|
+
const mm = String(d.getMinutes()).padStart(2, '0');
|
|
42
|
+
return `${dia}-${mes}-${anio} ${hh}:${mm}`;
|
|
43
|
+
}
|
|
44
|
+
/** Fecha larga para portadas. Ej: "15 de julio de 2026" */
|
|
45
|
+
export function fechaLarga(d = new Date()) {
|
|
46
|
+
return d.toLocaleDateString('es-CL', { day: 'numeric', month: 'long', year: 'numeric' });
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Formatea un RUT chileno con puntos y guion. Ej: "761234567" → "76.123.456-7"
|
|
50
|
+
* Si el valor no parece un RUT, se devuelve tal cual.
|
|
51
|
+
*/
|
|
52
|
+
export function rut(valor) {
|
|
53
|
+
if (!valor)
|
|
54
|
+
return '—';
|
|
55
|
+
const limpio = valor.replace(/[.\-\s]/g, '').toUpperCase();
|
|
56
|
+
if (!/^\d{7,8}[\dK]$/.test(limpio))
|
|
57
|
+
return valor;
|
|
58
|
+
const cuerpo = limpio.slice(0, -1);
|
|
59
|
+
const dv = limpio.slice(-1);
|
|
60
|
+
return `${Number(cuerpo).toLocaleString('es-CL')}-${dv}`;
|
|
61
|
+
}
|
|
62
|
+
/** Convierte horas a una glosa humana. Ej: 30.5 → "1d 6h" */
|
|
63
|
+
export function horasRestantes(horas) {
|
|
64
|
+
if (horas === null || horas === undefined || !Number.isFinite(horas) || horas <= 0)
|
|
65
|
+
return '—';
|
|
66
|
+
if (horas < 1)
|
|
67
|
+
return `${Math.round(horas * 60)} min`;
|
|
68
|
+
if (horas < 24)
|
|
69
|
+
return `${Math.round(horas)} h`;
|
|
70
|
+
const dias = Math.floor(horas / 24);
|
|
71
|
+
const resto = Math.round(horas % 24);
|
|
72
|
+
return resto > 0 ? `${dias}d ${resto}h` : `${dias}d`;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/reports/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell del documento: envuelve el cuerpo de un template en un HTML
|
|
3
|
+
* autocontenido y portable (CSS inline, sin recursos externos).
|
|
4
|
+
*
|
|
5
|
+
* Autocontenido a propósito: el informe debe poder moverse por correo o abrirse
|
|
6
|
+
* sin red y verse idéntico. Nada de CDNs ni fuentes remotas.
|
|
7
|
+
*/
|
|
8
|
+
import { type FormatoPapel } from './theme.js';
|
|
9
|
+
export interface DocumentoOpts {
|
|
10
|
+
titulo: string;
|
|
11
|
+
cuerpo: string;
|
|
12
|
+
/** Tamaño de papel. Por defecto Carta (estándar de oficina en Chile). */
|
|
13
|
+
formato?: FormatoPapel;
|
|
14
|
+
/** CSS adicional específico del template. */
|
|
15
|
+
cssExtra?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function renderDocumento(o: DocumentoOpts): string;
|
|
18
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/reports/render.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAuC,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAGpF,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAmBxD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell del documento: envuelve el cuerpo de un template en un HTML
|
|
3
|
+
* autocontenido y portable (CSS inline, sin recursos externos).
|
|
4
|
+
*
|
|
5
|
+
* Autocontenido a propósito: el informe debe poder moverse por correo o abrirse
|
|
6
|
+
* sin red y verse idéntico. Nada de CDNs ni fuentes remotas.
|
|
7
|
+
*/
|
|
8
|
+
import { baseCSS, PAPEL, FORMATO_POR_DEFECTO } from './theme.js';
|
|
9
|
+
import { esc } from './components.js';
|
|
10
|
+
export function renderDocumento(o) {
|
|
11
|
+
const formato = o.formato ?? FORMATO_POR_DEFECTO;
|
|
12
|
+
return `<!DOCTYPE html>
|
|
13
|
+
<html lang="es-CL">
|
|
14
|
+
<head>
|
|
15
|
+
<meta charset="utf-8">
|
|
16
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
17
|
+
<title>${esc(o.titulo)}</title>
|
|
18
|
+
<meta name="generator" content="mcp-compra-agil">
|
|
19
|
+
<meta name="formato-papel" content="${esc(PAPEL[formato].glosa)}">
|
|
20
|
+
<style>
|
|
21
|
+
${baseCSS(formato)}
|
|
22
|
+
${o.cssExtra ?? ''}
|
|
23
|
+
</style>
|
|
24
|
+
</head>
|
|
25
|
+
<body>
|
|
26
|
+
${o.cuerpo}
|
|
27
|
+
</body>
|
|
28
|
+
</html>`;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/reports/render.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAqB,MAAM,YAAY,CAAC;AACpF,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAWtC,MAAM,UAAU,eAAe,CAAC,CAAgB;IAC9C,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI,mBAAmB,CAAC;IACjD,OAAO;;;;;SAKA,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;;sCAEgB,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC;;EAE7D,OAAO,CAAC,OAAO,CAAC;EAChB,CAAC,CAAC,QAAQ,IAAI,EAAE;;;;EAIhB,CAAC,CAAC,MAAM;;QAEF,CAAC;AACT,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template: Informe de Radar de Oportunidades.
|
|
3
|
+
*
|
|
4
|
+
* Recibe datos ya recolectados (no consulta la API) — así el mismo dataset
|
|
5
|
+
* alimenta tanto la respuesta JSON de la tool como este informe.
|
|
6
|
+
*/
|
|
7
|
+
import { type TonoBadge } from '../components.js';
|
|
8
|
+
import { type FormatoPapel } from '../theme.js';
|
|
9
|
+
/** Forma mínima que el informe necesita de cada oportunidad. */
|
|
10
|
+
export interface OportunidadInforme {
|
|
11
|
+
codigo: string;
|
|
12
|
+
nombre: string;
|
|
13
|
+
organismo: string;
|
|
14
|
+
region: string;
|
|
15
|
+
presupuesto_disponible: number;
|
|
16
|
+
ofertas_recibidas: number;
|
|
17
|
+
horas_restantes: number;
|
|
18
|
+
fecha_cierre: string;
|
|
19
|
+
puntuacion_caliente: number;
|
|
20
|
+
factores_calificacion: string[];
|
|
21
|
+
}
|
|
22
|
+
export interface RadarInformeData {
|
|
23
|
+
oportunidades: OportunidadInforme[];
|
|
24
|
+
totalAnalizadas: number;
|
|
25
|
+
filtros: {
|
|
26
|
+
region?: string;
|
|
27
|
+
q?: string;
|
|
28
|
+
presupuestoMinimo?: number;
|
|
29
|
+
paginasEscaneadas: number;
|
|
30
|
+
};
|
|
31
|
+
generadoEn: Date;
|
|
32
|
+
/** Tamaño de papel del informe. Por defecto Carta. */
|
|
33
|
+
formato?: FormatoPapel;
|
|
34
|
+
}
|
|
35
|
+
/** Clasifica el hot score en un tono semántico. Máximo teórico: 105 pts. */
|
|
36
|
+
export declare function tonoScore(score: number): {
|
|
37
|
+
tono: TonoBadge;
|
|
38
|
+
glosa: string;
|
|
39
|
+
};
|
|
40
|
+
export declare function renderRadarInforme(data: RadarInformeData): string;
|
|
41
|
+
//# sourceMappingURL=radar-oportunidades.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radar-oportunidades.d.ts","sourceRoot":"","sources":["../../../src/reports/templates/radar-oportunidades.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAEL,KAAK,SAAS,EACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAsC,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEpF,gEAAgE;AAChE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB,EAAE,MAAM,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,kBAAkB,EAAE,CAAC;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,CAAC,CAAC,EAAE,MAAM,CAAC;QACX,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,UAAU,EAAE,IAAI,CAAC;IACjB,sDAAsD;IACtD,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAED,4EAA4E;AAC5E,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAK3E;AAQD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CA4JjE"}
|