@vint.tri/report_gen_mcp 1.0.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/dist/charts/bar.js +42 -0
- package/dist/charts/line.js +42 -0
- package/dist/charts/pie.js +41 -0
- package/dist/index.js +42 -0
- package/dist/utils/reportGenerator.js +59 -0
- package/generated-report.html +1 -0
- package/package.json +33 -0
- package/src/charts/bar.ts +42 -0
- package/src/charts/line.ts +42 -0
- package/src/charts/pie.ts +41 -0
- package/src/index.ts +43 -0
- package/src/utils/reportGenerator.ts +60 -0
- package/test-cli-report-fixed.html +14 -0
- package/test-cli-report.html +0 -0
- package/test-report.html +19 -0
- package/test-report.json +22 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.barSchema = void 0;
|
|
4
|
+
exports.renderBarChart = renderBarChart;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const chartjs_node_canvas_1 = require("chartjs-node-canvas");
|
|
7
|
+
exports.barSchema = zod_1.z.object({
|
|
8
|
+
labels: zod_1.z.array(zod_1.z.string()),
|
|
9
|
+
datasets: zod_1.z.array(zod_1.z.object({
|
|
10
|
+
label: zod_1.z.string(),
|
|
11
|
+
data: zod_1.z.array(zod_1.z.number()),
|
|
12
|
+
backgroundColor: zod_1.z.array(zod_1.z.string()).optional(),
|
|
13
|
+
})),
|
|
14
|
+
options: zod_1.z.object({
|
|
15
|
+
title: zod_1.z.string().optional(),
|
|
16
|
+
// Add more Chart.js options as needed
|
|
17
|
+
}).optional(),
|
|
18
|
+
});
|
|
19
|
+
async function renderBarChart(config) {
|
|
20
|
+
const chartConfig = {
|
|
21
|
+
type: 'bar',
|
|
22
|
+
data: {
|
|
23
|
+
labels: config.labels,
|
|
24
|
+
datasets: config.datasets,
|
|
25
|
+
},
|
|
26
|
+
options: {
|
|
27
|
+
...config.options,
|
|
28
|
+
plugins: {
|
|
29
|
+
title: config.options?.title ? {
|
|
30
|
+
display: true,
|
|
31
|
+
text: config.options.title
|
|
32
|
+
} : undefined
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const width = 800;
|
|
37
|
+
const height = 600;
|
|
38
|
+
const chartJSNodeCanvas = new chartjs_node_canvas_1.ChartJSNodeCanvas({ width, height });
|
|
39
|
+
const buffer = await chartJSNodeCanvas.renderToBuffer(chartConfig);
|
|
40
|
+
const base64Image = buffer.toString('base64');
|
|
41
|
+
return `<img src="data:image/png;base64,${base64Image}" alt="Chart" />`;
|
|
42
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.lineSchema = void 0;
|
|
4
|
+
exports.renderLineChart = renderLineChart;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const chartjs_node_canvas_1 = require("chartjs-node-canvas");
|
|
7
|
+
exports.lineSchema = zod_1.z.object({
|
|
8
|
+
labels: zod_1.z.array(zod_1.z.string()),
|
|
9
|
+
datasets: zod_1.z.array(zod_1.z.object({
|
|
10
|
+
label: zod_1.z.string(),
|
|
11
|
+
data: zod_1.z.array(zod_1.z.number()),
|
|
12
|
+
borderColor: zod_1.z.string().optional(),
|
|
13
|
+
})),
|
|
14
|
+
options: zod_1.z.object({
|
|
15
|
+
title: zod_1.z.string().optional(),
|
|
16
|
+
// Add more Chart.js options as needed
|
|
17
|
+
}).optional(),
|
|
18
|
+
});
|
|
19
|
+
async function renderLineChart(config) {
|
|
20
|
+
const chartConfig = {
|
|
21
|
+
type: 'line',
|
|
22
|
+
data: {
|
|
23
|
+
labels: config.labels,
|
|
24
|
+
datasets: config.datasets,
|
|
25
|
+
},
|
|
26
|
+
options: {
|
|
27
|
+
...config.options,
|
|
28
|
+
plugins: {
|
|
29
|
+
title: config.options?.title ? {
|
|
30
|
+
display: true,
|
|
31
|
+
text: config.options.title
|
|
32
|
+
} : undefined
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const width = 800;
|
|
37
|
+
const height = 600;
|
|
38
|
+
const chartJSNodeCanvas = new chartjs_node_canvas_1.ChartJSNodeCanvas({ width, height });
|
|
39
|
+
const buffer = await chartJSNodeCanvas.renderToBuffer(chartConfig);
|
|
40
|
+
const base64Image = buffer.toString('base64');
|
|
41
|
+
return `<img src="data:image/png;base64,${base64Image}" alt="Chart" />`;
|
|
42
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pieSchema = void 0;
|
|
4
|
+
exports.renderPieChart = renderPieChart;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const chartjs_node_canvas_1 = require("chartjs-node-canvas");
|
|
7
|
+
exports.pieSchema = zod_1.z.object({
|
|
8
|
+
labels: zod_1.z.array(zod_1.z.string()),
|
|
9
|
+
datasets: zod_1.z.array(zod_1.z.object({
|
|
10
|
+
data: zod_1.z.array(zod_1.z.number()),
|
|
11
|
+
backgroundColor: zod_1.z.array(zod_1.z.string()).optional(),
|
|
12
|
+
})),
|
|
13
|
+
options: zod_1.z.object({
|
|
14
|
+
title: zod_1.z.string().optional(),
|
|
15
|
+
// Add more Chart.js options as needed
|
|
16
|
+
}).optional(),
|
|
17
|
+
});
|
|
18
|
+
async function renderPieChart(config) {
|
|
19
|
+
const chartConfig = {
|
|
20
|
+
type: 'pie',
|
|
21
|
+
data: {
|
|
22
|
+
labels: config.labels,
|
|
23
|
+
datasets: config.datasets,
|
|
24
|
+
},
|
|
25
|
+
options: {
|
|
26
|
+
...config.options,
|
|
27
|
+
plugins: {
|
|
28
|
+
title: config.options?.title ? {
|
|
29
|
+
display: true,
|
|
30
|
+
text: config.options.title
|
|
31
|
+
} : undefined
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
const width = 800;
|
|
36
|
+
const height = 600;
|
|
37
|
+
const chartJSNodeCanvas = new chartjs_node_canvas_1.ChartJSNodeCanvas({ width, height });
|
|
38
|
+
const buffer = await chartJSNodeCanvas.renderToBuffer(chartConfig);
|
|
39
|
+
const base64Image = buffer.toString('base64');
|
|
40
|
+
return `<img src="data:image/png;base64,${base64Image}" alt="Chart" />`;
|
|
41
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const reportGenerator_1 = require("./utils/reportGenerator");
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const app = (0, express_1.default)();
|
|
12
|
+
const port = 3000;
|
|
13
|
+
app.use(express_1.default.json());
|
|
14
|
+
app.post('/generate-report', async (req, res) => {
|
|
15
|
+
const { document, charts, outputFile = 'report.html' } = req.body;
|
|
16
|
+
try {
|
|
17
|
+
const result = await (0, reportGenerator_1.generateReport)(document, charts, path_1.default.resolve(outputFile));
|
|
18
|
+
res.json(result);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
res.status(500).json({ error: error.message });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
commander_1.program
|
|
25
|
+
.command('start-server')
|
|
26
|
+
.description('Start the MCP report generation server')
|
|
27
|
+
.action(() => {
|
|
28
|
+
app.listen(port, () => {
|
|
29
|
+
console.log(`Server running at http://localhost:${port}`);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
commander_1.program
|
|
33
|
+
.command('generate')
|
|
34
|
+
.option('--document <md>', 'Markdown document with placeholders [[chart:id]]')
|
|
35
|
+
.option('--charts <json>', 'JSON string of charts {id: {type: "bar", config: {...}}}')
|
|
36
|
+
.option('--output <file>', 'Output HTML file', 'report.html')
|
|
37
|
+
.action(async (opts) => {
|
|
38
|
+
const charts = JSON.parse(opts.charts);
|
|
39
|
+
const result = await (0, reportGenerator_1.generateReport)(opts.document, charts, opts.output);
|
|
40
|
+
console.log(result);
|
|
41
|
+
});
|
|
42
|
+
commander_1.program.parse();
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateReport = generateReport;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const ejs_1 = __importDefault(require("ejs"));
|
|
9
|
+
const marked_1 = require("marked");
|
|
10
|
+
const bar_1 = require("../charts/bar");
|
|
11
|
+
const line_1 = require("../charts/line");
|
|
12
|
+
const pie_1 = require("../charts/pie");
|
|
13
|
+
// Add more imports for other chart types as needed
|
|
14
|
+
const chartRenderers = {
|
|
15
|
+
bar: { schema: bar_1.barSchema, renderer: bar_1.renderBarChart },
|
|
16
|
+
line: { schema: line_1.lineSchema, renderer: line_1.renderLineChart },
|
|
17
|
+
pie: { schema: pie_1.pieSchema, renderer: pie_1.renderPieChart },
|
|
18
|
+
// Add more chart types here
|
|
19
|
+
};
|
|
20
|
+
async function generateReport(document, charts, outputFile) {
|
|
21
|
+
// Validate charts
|
|
22
|
+
for (const [id, { type, config }] of Object.entries(charts)) {
|
|
23
|
+
const { schema } = chartRenderers[type];
|
|
24
|
+
if (!schema)
|
|
25
|
+
throw new Error(`Unsupported chart type: ${type}`);
|
|
26
|
+
schema.parse(config);
|
|
27
|
+
}
|
|
28
|
+
// Render charts to HTML img tags
|
|
29
|
+
const renderedCharts = {};
|
|
30
|
+
for (const [id, { type, config }] of Object.entries(charts)) {
|
|
31
|
+
const htmlImg = await chartRenderers[type].renderer(config);
|
|
32
|
+
renderedCharts[id] = htmlImg;
|
|
33
|
+
}
|
|
34
|
+
// Replace placeholders in Markdown, e.g., [[chart:id]]
|
|
35
|
+
let mdWithCharts = document;
|
|
36
|
+
for (const [id, htmlImg] of Object.entries(renderedCharts)) {
|
|
37
|
+
mdWithCharts = mdWithCharts.replace(new RegExp(`\\[\\[chart:${id}\\]\\]`, 'g'), htmlImg);
|
|
38
|
+
}
|
|
39
|
+
// Convert Markdown to HTML
|
|
40
|
+
const htmlContent = await (0, marked_1.marked)(mdWithCharts);
|
|
41
|
+
// Wrap in full HTML template
|
|
42
|
+
const template = `
|
|
43
|
+
<!DOCTYPE html>
|
|
44
|
+
<html lang="en">
|
|
45
|
+
<head>
|
|
46
|
+
<meta charset="UTF-8">
|
|
47
|
+
<title>Report</title>
|
|
48
|
+
<style> body { font-family: Arial, sans-serif; } img { max-width: 100%; } </style>
|
|
49
|
+
</head>
|
|
50
|
+
<body>
|
|
51
|
+
<%- htmlContent %>
|
|
52
|
+
</body>
|
|
53
|
+
</html>
|
|
54
|
+
`;
|
|
55
|
+
const fullHtml = ejs_1.default.render(template, { htmlContent }, { escape: (str) => str });
|
|
56
|
+
// Save to file
|
|
57
|
+
await fs_extra_1.default.writeFile(outputFile, fullHtml);
|
|
58
|
+
return { success: true, filePath: outputFile };
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"success":true,"filePath":"/Applications/Python/report_gen_mcp/test-report.html"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vint.tri/report_gen_mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool for generating HTML reports with embedded charts",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"report_gen_mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"start": "node dist/index.js",
|
|
12
|
+
"test": "echo \"No tests yet\""
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"chart.js": "^3.9.1",
|
|
16
|
+
"chartjs-node-canvas": "^4.1.6",
|
|
17
|
+
"commander": "^12.1.0",
|
|
18
|
+
"ejs": "^3.1.10",
|
|
19
|
+
"express": "^4.19.2",
|
|
20
|
+
"fs-extra": "^11.2.0",
|
|
21
|
+
"marked": "^13.0.2",
|
|
22
|
+
"zod": "^3.23.8"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/ejs": "^3.1.5",
|
|
26
|
+
"@types/express": "^4.17.21",
|
|
27
|
+
"@types/fs-extra": "^11.0.4",
|
|
28
|
+
"@types/node": "^22.4.1",
|
|
29
|
+
"typescript": "^5.5.4"
|
|
30
|
+
},
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"author": ""
|
|
33
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ChartConfiguration } from 'chart.js';
|
|
3
|
+
import { ChartJSNodeCanvas } from 'chartjs-node-canvas';
|
|
4
|
+
|
|
5
|
+
export const barSchema = z.object({
|
|
6
|
+
labels: z.array(z.string()),
|
|
7
|
+
datasets: z.array(z.object({
|
|
8
|
+
label: z.string(),
|
|
9
|
+
data: z.array(z.number()),
|
|
10
|
+
backgroundColor: z.array(z.string()).optional(),
|
|
11
|
+
})),
|
|
12
|
+
options: z.object({
|
|
13
|
+
title: z.string().optional(),
|
|
14
|
+
// Add more Chart.js options as needed
|
|
15
|
+
}).optional(),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export async function renderBarChart(config: z.infer<typeof barSchema>): Promise<string> {
|
|
19
|
+
const chartConfig: ChartConfiguration = {
|
|
20
|
+
type: 'bar',
|
|
21
|
+
data: {
|
|
22
|
+
labels: config.labels,
|
|
23
|
+
datasets: config.datasets,
|
|
24
|
+
},
|
|
25
|
+
options: {
|
|
26
|
+
...config.options,
|
|
27
|
+
plugins: {
|
|
28
|
+
title: config.options?.title ? {
|
|
29
|
+
display: true,
|
|
30
|
+
text: config.options.title
|
|
31
|
+
} : undefined
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const width = 800;
|
|
37
|
+
const height = 600;
|
|
38
|
+
const chartJSNodeCanvas = new ChartJSNodeCanvas({ width, height });
|
|
39
|
+
const buffer = await chartJSNodeCanvas.renderToBuffer(chartConfig);
|
|
40
|
+
const base64Image = buffer.toString('base64');
|
|
41
|
+
return `<img src="data:image/png;base64,${base64Image}" alt="Chart" />`;
|
|
42
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ChartConfiguration } from 'chart.js';
|
|
3
|
+
import { ChartJSNodeCanvas } from 'chartjs-node-canvas';
|
|
4
|
+
|
|
5
|
+
export const lineSchema = z.object({
|
|
6
|
+
labels: z.array(z.string()),
|
|
7
|
+
datasets: z.array(z.object({
|
|
8
|
+
label: z.string(),
|
|
9
|
+
data: z.array(z.number()),
|
|
10
|
+
borderColor: z.string().optional(),
|
|
11
|
+
})),
|
|
12
|
+
options: z.object({
|
|
13
|
+
title: z.string().optional(),
|
|
14
|
+
// Add more Chart.js options as needed
|
|
15
|
+
}).optional(),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export async function renderLineChart(config: z.infer<typeof lineSchema>): Promise<string> {
|
|
19
|
+
const chartConfig: ChartConfiguration = {
|
|
20
|
+
type: 'line',
|
|
21
|
+
data: {
|
|
22
|
+
labels: config.labels,
|
|
23
|
+
datasets: config.datasets,
|
|
24
|
+
},
|
|
25
|
+
options: {
|
|
26
|
+
...config.options,
|
|
27
|
+
plugins: {
|
|
28
|
+
title: config.options?.title ? {
|
|
29
|
+
display: true,
|
|
30
|
+
text: config.options.title
|
|
31
|
+
} : undefined
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const width = 800;
|
|
37
|
+
const height = 600;
|
|
38
|
+
const chartJSNodeCanvas = new ChartJSNodeCanvas({ width, height });
|
|
39
|
+
const buffer = await chartJSNodeCanvas.renderToBuffer(chartConfig);
|
|
40
|
+
const base64Image = buffer.toString('base64');
|
|
41
|
+
return `<img src="data:image/png;base64,${base64Image}" alt="Chart" />`;
|
|
42
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ChartConfiguration } from 'chart.js';
|
|
3
|
+
import { ChartJSNodeCanvas } from 'chartjs-node-canvas';
|
|
4
|
+
|
|
5
|
+
export const pieSchema = z.object({
|
|
6
|
+
labels: z.array(z.string()),
|
|
7
|
+
datasets: z.array(z.object({
|
|
8
|
+
data: z.array(z.number()),
|
|
9
|
+
backgroundColor: z.array(z.string()).optional(),
|
|
10
|
+
})),
|
|
11
|
+
options: z.object({
|
|
12
|
+
title: z.string().optional(),
|
|
13
|
+
// Add more Chart.js options as needed
|
|
14
|
+
}).optional(),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export async function renderPieChart(config: z.infer<typeof pieSchema>): Promise<string> {
|
|
18
|
+
const chartConfig: ChartConfiguration = {
|
|
19
|
+
type: 'pie',
|
|
20
|
+
data: {
|
|
21
|
+
labels: config.labels,
|
|
22
|
+
datasets: config.datasets,
|
|
23
|
+
},
|
|
24
|
+
options: {
|
|
25
|
+
...config.options,
|
|
26
|
+
plugins: {
|
|
27
|
+
title: config.options?.title ? {
|
|
28
|
+
display: true,
|
|
29
|
+
text: config.options.title
|
|
30
|
+
} : undefined
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const width = 800;
|
|
36
|
+
const height = 600;
|
|
37
|
+
const chartJSNodeCanvas = new ChartJSNodeCanvas({ width, height });
|
|
38
|
+
const buffer = await chartJSNodeCanvas.renderToBuffer(chartConfig);
|
|
39
|
+
const base64Image = buffer.toString('base64');
|
|
40
|
+
return `<img src="data:image/png;base64,${base64Image}" alt="Chart" />`;
|
|
41
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import express from 'express';
|
|
4
|
+
import { program } from 'commander';
|
|
5
|
+
import { generateReport } from './utils/reportGenerator';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
|
|
8
|
+
const app = express();
|
|
9
|
+
const port = 3000;
|
|
10
|
+
|
|
11
|
+
app.use(express.json());
|
|
12
|
+
|
|
13
|
+
app.post('/generate-report', async (req, res) => {
|
|
14
|
+
const { document, charts, outputFile = 'report.html' } = req.body;
|
|
15
|
+
try {
|
|
16
|
+
const result = await generateReport(document, charts, path.resolve(outputFile));
|
|
17
|
+
res.json(result);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
res.status(500).json({ error: (error as Error).message });
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
program
|
|
24
|
+
.command('start-server')
|
|
25
|
+
.description('Start the MCP report generation server')
|
|
26
|
+
.action(() => {
|
|
27
|
+
app.listen(port, () => {
|
|
28
|
+
console.log(`Server running at http://localhost:${port}`);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
program
|
|
33
|
+
.command('generate')
|
|
34
|
+
.option('--document <md>', 'Markdown document with placeholders [[chart:id]]')
|
|
35
|
+
.option('--charts <json>', 'JSON string of charts {id: {type: "bar", config: {...}}}')
|
|
36
|
+
.option('--output <file>', 'Output HTML file', 'report.html')
|
|
37
|
+
.action(async (opts) => {
|
|
38
|
+
const charts = JSON.parse(opts.charts);
|
|
39
|
+
const result = await generateReport(opts.document, charts, opts.output);
|
|
40
|
+
console.log(result);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
program.parse();
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import ejs from 'ejs';
|
|
3
|
+
import { marked } from 'marked';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { barSchema, renderBarChart } from '../charts/bar';
|
|
6
|
+
import { lineSchema, renderLineChart } from '../charts/line';
|
|
7
|
+
import { pieSchema, renderPieChart } from '../charts/pie';
|
|
8
|
+
// Add more imports for other chart types as needed
|
|
9
|
+
|
|
10
|
+
const chartRenderers: Record<string, { schema: z.ZodObject<any>; renderer: (config: any) => Promise<string> }> = {
|
|
11
|
+
bar: { schema: barSchema, renderer: renderBarChart },
|
|
12
|
+
line: { schema: lineSchema, renderer: renderLineChart },
|
|
13
|
+
pie: { schema: pieSchema, renderer: renderPieChart },
|
|
14
|
+
// Add more chart types here
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export async function generateReport(document: string, charts: Record<string, { type: string; config: any }>, outputFile: string) {
|
|
18
|
+
// Validate charts
|
|
19
|
+
for (const [id, { type, config }] of Object.entries(charts)) {
|
|
20
|
+
const { schema } = chartRenderers[type];
|
|
21
|
+
if (!schema) throw new Error(`Unsupported chart type: ${type}`);
|
|
22
|
+
schema.parse(config);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Render charts to HTML img tags
|
|
26
|
+
const renderedCharts: Record<string, string> = {};
|
|
27
|
+
for (const [id, { type, config }] of Object.entries(charts)) {
|
|
28
|
+
const htmlImg = await chartRenderers[type].renderer(config);
|
|
29
|
+
renderedCharts[id] = htmlImg;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Replace placeholders in Markdown, e.g., [[chart:id]]
|
|
33
|
+
let mdWithCharts = document;
|
|
34
|
+
for (const [id, htmlImg] of Object.entries(renderedCharts)) {
|
|
35
|
+
mdWithCharts = mdWithCharts.replace(new RegExp(`\\[\\[chart:${id}\\]\\]`, 'g'), htmlImg);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Convert Markdown to HTML
|
|
39
|
+
const htmlContent = await marked(mdWithCharts);
|
|
40
|
+
|
|
41
|
+
// Wrap in full HTML template
|
|
42
|
+
const template = `
|
|
43
|
+
<!DOCTYPE html>
|
|
44
|
+
<html lang="en">
|
|
45
|
+
<head>
|
|
46
|
+
<meta charset="UTF-8">
|
|
47
|
+
<title>Report</title>
|
|
48
|
+
<style> body { font-family: Arial, sans-serif; } img { max-width: 100%; } </style>
|
|
49
|
+
</head>
|
|
50
|
+
<body>
|
|
51
|
+
<%- htmlContent %>
|
|
52
|
+
</body>
|
|
53
|
+
</html>
|
|
54
|
+
`;
|
|
55
|
+
const fullHtml = ejs.render(template, { htmlContent }, { escape: (str) => str });
|
|
56
|
+
|
|
57
|
+
// Save to file
|
|
58
|
+
await fs.writeFile(outputFile, fullHtml);
|
|
59
|
+
return { success: true, filePath: outputFile };
|
|
60
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<title>Report</title>
|
|
7
|
+
<style> body { font-family: Arial, sans-serif; } svg { max-width: 100%; } </style>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<h1>Test Report\n\nThis is a test report.\n\n<img src="data:image/png;base64,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" alt="Chart" /></h1>
|
|
11
|
+
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
14
|
+
|
|
Binary file
|
package/test-report.html
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<title>Report</title>
|
|
7
|
+
<style> body { font-family: Arial, sans-serif; } img { max-width: 100%; } </style>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<h1>Sales Report</h1>
|
|
11
|
+
<p>This is our monthly sales report.</p>
|
|
12
|
+
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAYAAACadoJwAAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzdeXidV33g8d9575XkxE5iO4QQZ4GwNUBJCcu0ZYcCbUmBshhIG8DY0islxmxdYGBo1RYYOtMWGDex9MoBsy+m67TQpmEZ9oa1hUJLgJIEOyQhcVicWNt75o9IGeFxEjnWPY7M5/M8fh7f+5577nnl81zp67soAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuMX69es7GzZsWHG41wHA8pUO9wIAjmRDQ0P3TSl9KCIipfSX4+PjvzN/rK7rd0TEwyMiut3uz1144YU/PoT7eVBK6T7dbvdDF1544Y+Hhob+OqV0t6ZpfmH/sXVd/0lEPH1qauohO3bsuGEx89d1fVpK6YKc8y9HRF9EfCOl9Efj4+PvvK3bDQ4OPr6qqomIeE3TNO++I+e20PDw8Dk559cuuGpvRHwt57xtYmLi/9ze7c8///xTZ2ZmfmF2dvbSiy666PJDXQ8AB6863AsAOJJ1Op3+iLhnRNwz5/zCuq77IiI2bNiwOiKePX+s0+kc0uNxSukFEfH+ycnJu81dPjki7n6gsTnnEw7yPlNE/O+c8xMj4nUR8dsR0Z9zftvIyMhjb2ddR0fEPVNKxy7yvm5TzvnYuPlrliPi23Nre3ZK6cPDw8NPu73bT09PPyIi3t/tdh+1FOsB4OB1D/cCAH5KTEXE8TnnJ0TEh/r6+s6OiP6I2BcRt7ykacOGDav7+vrOSyn9TER8p9vtjl144YXf27x58/FTU1O/ExEfnTv24Ij44p49ey5YvXr1UyPikRERnU7nd4aGhrbPz1fX9SMj4nkppR+mlN40Nja2a/5YSumUoaGh342If56YmPirufHn5ZzXnXzyyb8/OjraRkRs2bLlmMnJyTMj4l+bpvnDiMhDQ0OfTyltnp2dvWtExMjIyD3ath3KOZ9eVdWelNLOsbGxj+3/RdiyZcvA1NRUnXN+WErpuznnrU3TXDV332enlJ6Wcz4qpfSlo48+etsb3/jGm27l6/nWpmleFxExNDT07JTS+3LO/zMi/mbuumemlH4lIo5OKX29qqoL2ra9V875nLnbP2d4ePjG8fHxvxweHn5izvnpEbEm5/zNTqcztvDrBMDS8gwIQAEppYsjok0pPScioqqqZ0bEDRHxqfkxdV0f19/f/+WU0u/knFdGxMjMzMxXBwcHT5menl6dUnpFSum9EfHMiHhERLx5zZo1m6uqOiEijpub5tSc8zFzf18dEeM55/vmnH97dnb2fQvX1O12v5dSen5K6Q0RERs3bjwmIv4spXTmfHxERGzduvWHEfHFiDizruuvDQ8P/8+IOHrPnj3nTkxMvL+u6+Patv3niHhuVVWX5Zx/sW3bD9d1/eCF9zc6OtqdnJz8aM75D+LmZ1CeHxH/vHHjxmOGhoZ+OSL+Lue8OiKuzjn/0d69e7ct5ms7MTHx/oj4ekTcZ+PGjeuGh4e3pJQ+kFI6Nud8Zc7592dnZ9+fUlqVUrprRETbtnfNOZ8wPDz8jJzzP0bE6RFxWUrppW3bXrKY+wXgjhEgAAW0bfvdnPPHI+JpmzZtWptz/uWU0l/Hzc+AzKsj4u4553MmJibWt237pIg4vqqql88PSCn9c9M0j6mq6olzV/38+Ph4ExF/HxExOzv74u3bt39k7lg3Ih4zMTHxuJzzpSml/xIL3vs3PT09k1J6V0Tcd3Bw8MxOp/OrEbEipfS2/dc/MzPzlIh4a0SclHP+7ZTSB9esWfMfdV2fETc/y/Catm2fPvf+jEvi5u8v91o4x65du54REb+YUjqv0+mcl1J6cUSc2u12X1BV1QMiInLO1+Wc3xoRvxYROw/iS7wrIqLT6axq2/YbETE0Pj7+3JTSmyPi+oi4z9jY2Mfatn1zRERVVVubphnPOV+TUhq56aabnjU7O/umiLgiIu5zEPcLwEESIACFzD17sbqqqjdGxNE55/fsN+SMiIhut3tpRMT27dv/NW4OlDPmB+ScL4uIaNt219ycR93GXV7fNM33IyKqqtoVEX3r16/v229Nb5s7/qyU0jMi4rrrr7/+7xaO2bhx4zEDAwMrpqamXr5u3bq7RMSjIuK9EXGPiPiziPh+zvm+VVW9P27+Yf/ZB1rMgsh47+zs7PU557+YW8P9qqp6a0S8b+4Zma9GxPvm5l+se0REOz09fUVEXJ9SelZd19dExBci4la/Rp1O59qc868eddRRl3U6nW9ExIkHcZ8A3AECBKCQvr6+D0TEdErp+RFxzbp16z6y35BdERGzs7OnR0TUdX1S3PyMxHfnB6SUZm/rPvr7+zsLLt7yMqqccz7Q+LGxsa+mlL4QEc+NiCdHxHt27tw5td+6Hzs7O/ut/v7+raOjozNN03yyqqqXzB1emXN+XkT8VkSM7dmzZ3VE/PcD3Vfbtt+NiKiq6nFN06R169b1tW370E6n80czMzP3Til9YGpq6vi2bX8pbg6ZN7/sZS+7rcCKiIi6rl8cEffOOX9mx44d+1JKb8s5379t27OaplkXEVcf4GZVRMTMzMzWiHhCRDy+aZq75Jy/cnv3B8ChESAAhVxwwQXXpZT+MSIipfSB0dHRmYXHq6p6S9z8sbLvqOv6VXHzG6pncs5ji5j+RxE3/0A9MjLyiINc2tvi5pcdHXOgl18dffTRl0TElyLi3KGhoYvruh5r2/Zjc+fx9oiYj557rV279ukR8eKIiJzzwhiKbrf7gYi4um3bbcPDw6/YvXv331dV9ZmZmZkTUkoPzTnv7O/vf01K6bi4+aN+r7yNN6G/vK7rb9V1fUNEvDkibuh0OlvmjlURcVRK6eeHhoZ+L25+KVh3br0/mlvbbw0NDT0vpVTNHXtwXdcvTSk9PCKq0dFR3x8BesQDLEAPVVX1g7j5vQxfjIjIOW+NiJ0554vmhnwyInZ2u93psbGx77Rt+/C5lyA9OyJ25Zwf0zTNF9u2/XFE7Gzb9ssREXv27GnnLn967n7GI+LdKaU09yb0S1JK/3t+HTnnz0TEzgc84AFtSulzEbFzYGBgau7Y38wN+7fx8fHP738OcxHwpJzzH6eUVkfEY1NKV0TEs8fHxy9asWLF2yNiW0Q8um3bF0bEG+fO+ccppd1zf//Wtm3b9nQ6nYdHxOdyzs+NiDbn/OSmab7SNM1YSum3cs6PSSn9UUR8LqV09v5ryTl/a26+D8fNL6+6OCJe3+12zxwbG/vS3LDBiPhKSulVKaVVEfH6nPPH169f3xkYGLhk7n0h16SU7lJV1Usj4mMR8TsRcd+I+IOI+MDVV199XADQE34RIcBPsaGhoaGIODul9LSIeFnTNG863GsC4Mjm94AA/BRLKT0kIs5IKU309/cv6mNvAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoNfS4V4AALctD76ujpwferjXcatSO562/94XDvcyAFgeuod7AQDcjpyfEBHrD/cyblXbuSQiBAgAiyJAAOiZLVu2DExOTv5uzvnXUkorI+LzEfFHTdN867ZuV9f1eTnnyyYmJi4ps1IASqkO9wIAOHJNTk7+r4h4aKfT2dTpdJ4aEVdHxD9t2LBhxe3c9KEppXv1foUAlOYZEAB66eyIeN7Y2NhX5y6/oq7rh/f19Z0REV/esmXLwNTU1Nk553Uppcuvv/76D+7cuXN2/0nqun5wRDws5/ydiYmJf5y7Og0PDz8+53xGROzas2fPB3fu3DlV5rQAuKM8AwJAL30hIl47PDz8xLquj46IaJrmURMTE18eHR2tJicnP5Fz/o2IOC7n/Idr1qz5/f0nqOv6lRFxYUTcLaX0B3Vd/3FExPDw8Otyzn8YEcfknM9fs2bNuwueFwB3kAABoGcGBgael3P+SM75TRFxQ13XnxgeHj4nIuLyyy8/PiI+3DTNs5qmeV1K6X9ExC8uvP3IyMjJEbE5Ih7fNM0frFy58nERcU5d16dFxOMi4gNN07xh1apVT0spfbrs2QFwR3gJFgA9s3Xr1h9GxGsi4jWbNm1a2+12n5Zz3lrXdWqa5t3Dw8Mfquv6zTnn++WcT0sp7Vp4+7ZtHxQRx6WUPj48PBw33nhjxM3Pljws5/zKlNJ4Xdeb9+7d+39yzhcdhlME4CB5BgSAnqjr+oy6rq9cv359JyLioosuun58fPytEbEjpfTEkZGRR+Sc31FV1ftXrFjxlJzzyw4wzQ8i4j+rqnr2/J+U0qO73e5Hpqen/6Vpmvu1bfvEnPO/ppT+YXh4+D5FTxKAgyZAAOiJPXv2XBYRe9asWfP6+U+9GhkZuWtE/FJEfHF2dvb+EfGtsbGxT0dEpJTOzTl3Fs4xNTX1+Yg4dmZm5p7btm379vT09FTO+R9yzn39/f0fHxoaesr27dv/M6U0ERF7Zmdnjy17lgAcLAECQE/s3LlzdnZ29ik555/p7+//Xl3Xl7dt++WU0l+Mj4//+czMzF+nlI6q63rX5OTkpTnnL0XEmcPDw0/JOV+bUvrhjh079kXEMyPidXVdX1FV1Udzzi8ZGxu7pqqq81JKr6vr+vKI+HpEvGP79u1fPKwnDcDtSod7AQDctrzpte+PO/NvQs/pOektr37/bQ0ZHR2trrvuulVz7wkB4KeYN6ED3Oml8Uhx5/2N4Ln6/O0NGR0dbSNCfAAAAAAAAAAAAAAAAAAAAAAA/89PfAzv5s2bj5+ent6YUjq5bdtPTExM/GVE5IiI4eHhLRFx9PzYqqr+etu2bf8xd+xZEfHIiLhqZmZm4qKLLrq+3CkAAADLxS2/iLCu66Onp6cvjYi75JwvTSm9pq7rP4iI2Lhx4zE551cfaIK6rl+Vc/5vbdt+LiJO63Q6n9qyZctAmeUDAADLyS2/BySl9LSIuGx8fPwVERGDg4PfrqrqPRHxe319fT+bc750fHz8jw8wx5ac81MnJiY+FxHvquv6y/v27XtiRPxdkTMAAACWjYUB8pW2bV87f7nT6azNOf8oIiLnfGZE3Dg8PNy0bTsQEe+dmJj4UF3Xd4mIu9xwww1fXDDPpRHxsyFAAACA/dwSIGNjY1+d/3td12fknP9XSunVEREppftFxAk557dUVXW3nPPbhoaGNrZt+29VVd20c+fO2fnbtm37o5TSMQe4r5NuZy3Hht+SS2/ZY/SaPUav2WP0mj1Gz3UXXli/fn1n9erVL4uIF+WcX9Y0zV9FRIyPj7904bi6ro9NKW3o6+t72czMTN/CYyml/pTSjw9wX1fdzlpOjIj/OPhTgINye/sQDpU9Rq/ZY/SaPUZPVQv+ntasWfO+lNJZU1NTD5qYmPir+QNDQ0PP3rRp09pbBqZ0XUSsvvbaa6+OiE5d1wuf3Ti9bdvv9n7pAADAcrPwU7CelXM+tmmac3fs2HHDwkEppWdUVfXq0dHR6nnPe97Ktm3rlNIHd+7cORURl6SUXj43xwMj4rFt215c9jQAAIDlYOFLsH4upXSvuq6/ueC6y5umeXy3233p7OzsxO7du7971FFH3RQRH8g5b42IaNt2c1VVO+q6viIiZlNK51900UWXlzwJAACAg/Wgw70Ajni390EIcKjsMXrNHqPX7DF6rrr9IQAAAEtDgAAAAMUIEAAAoBgBAgAAFCNAAACAYgQIAABQjAABAACKESAAAEAxAgQAAChGgAAAAMUIEAAAoBgBAgAAFCNAAACAYgQIAABQjAABAACKESAAAEAxAgQAAChGgAAAAMUIEAAAoBgBAgAAFCNAAACAYgQIAABQjAABAACKESAAAEAxAgQAAChGgAAAAMUIEAAAoBgBAgAAFCNAAACAYgQIAABQjAABAACKESAAAEAxAgQAAChGgAAAAMUIEAAAoBgBAgAAFCNAAACAYgQIAABQjAABAACKESAAAEAxAgQAAChGgAAAAMUIEAAAoBgBAgAAFNM93AsAAODIdtHGfMzkgJ87l5t9K2Lfy9+YblrqeW0EAAB6arYTf9uZjcce7nVwcFbujT+NiN9e6nm9BAsAAChGgAAAAMUIEAAAoBgBAgAAFCNAAACAYgQIAABQjAABAACKESAAAEAxAgQAAChGgAAAAMUIEAAAoBgBAgAAFCNAAACAYgQIAABQjAABAACKESAAAEAxAgQAAChGgAAAAMUIEAAAoBgBAgAAFCNAAACAYgQIAABQjAABAACKESAAAEAxAgQAAChGgAAAAMUIEAAAoBgBAgAAFCNAAACAYgQIAABQjAABAACKESAAAEAxAgQAAChGgAAAAMUIEAAAoBgBAgAAFCNAAACAYgQIAABQjAABAACKESAAAEAxAgQAAChGgAAAAMUIEAAAoBgBAgAAFCNAAACAYroLL2zZsuXYffv2nRsRJ0XEpycmJj40f2xoaOi+EfHMlFKVc/7riYmJf5s/Njw8/OS2bX8xpXT1zMzM297ylrf8qNgZAAAAy8Ytz4Bs2LBhxeTk5D+nlO6fUro8pfRndV2/OiKiruszUkqfSSmliJhKKX18ZGTkrIiI4eHhl+ec/yQirkgpPaTb7X6iruu+w3M6AADAndktz4D09fU9NSJ2NU3zooiIkZGRr7dt+/aIeF1EnBcRb22a5vUREXVdr2zb9kURsSnn/PKU0vqmaT4TERN1XX815/yEiPjQ/3dvAADAT7VbngGpqurbEfE/5i+3bbsyIm6au/jAlNJn54/N/f2BmzdvPj4i7nb99ddfumDOz1RVdWZvlw0AACxHtzwDMj4+/vn5vw8ODp4eEW+OiDdERKSUjs05/3D+eNu2P0wpHTs5OXlsVVU37ty5c3bBnD/KOR97gPs6aRHrWcwYuKNOPNwL4Ii35HvsFS+4/9rfesF9zlvqeem9N73rG2Ovv+jr1y3xtB7H6LWe7LHp2X393e6KXkxND+2dvG5l9ODn8+5+l1Nd1yMR8cqc86smJibeFRHRtu1sSmnh+zr6ImK2v79/ZmZmZv85+lJKB3oT+lW3s5YTFzEGDpU9Rq8t6R57w0vvtypyZ/NSzkkZrzvv/n/++ou+3ovHHI9j9NqS77G+zoqpvNST0nMrB47fGz3YDws/hjfVdf22iHhSVVUPm4+PiIiU0u6c891vuVFV3T0idnc6nWsiom9wcHBhLd8957x7qRcKAAAsf7cEyNDQ0K/nnE9dt27dM8fGxq7Zb9w/pJSGtmzZMlDXdV/OeTgi/mHr1q2TOeePVlW1eW6O+0bE4yLiknKnAAAALBe3vHwqpfSoiLjv7t27L6vrev7qy5umefy6desu2r179y9OTk5+MyJyRHx6YGDgz+fGvCil9O66rs+JiKMi4rebpvlWyZMAAACWh1sCpGmal0fEyw80aHR0dCYiNoyOjnZ3796dmqaZnj82MTHxjYh46IYNG1bs2LFjX89XDAAALFv7v4H8Ns2FyAGJDwAA4PZUtz8EAABgaQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAU0z3cCwAADq9f/+hHV093u+lwr4ODc/TatXt3PuABU4d7HXCwBAgA/JSbTunymJ099nCvg4Nz0/e/f25EvOtwrwMOlpdgAQAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFBM90BXDg0NPb3b7X5t27Zt/zF/3fDw8LkRcfT85dnZ2X/avn37f86Nf0xK6RE55++llN7bNM2NPV85AACw7Px/z4CMjIw8IqU03rbtveavq+v66Jzzn7Vte8/5P1VVrZo7tjmldFFE3FhV1S9HxMdGR0cPGDYAAMBPt58IhbquP9u27QkRsWrh9TnnB6SUPj8xMfHKA8zxuxHxm03TfDIi3lzX9dd27979+Ii4uGerBgAAlqWfCJCmaX4hImJoaOjilNIt11dVdWbO+Zq6rl+XUupPKe0cGxu7dNOmTWsj4uR169Z9dm5ozjl/KqX0oBAgAADAfhb1Uqmc85kppZ+NiEsi4q5t2/7jyMjI02dmZi6PiBtHR0dn5semlH4YEcceYJqTFnFXixkDd9SJSz3hz7z07fdbee+HPWOp56XX0vQXX3TG/+jBxEu+x97591eccO6TT1/qaSngvRdfeUJE/GCJp13yPRYRETmnWPAfjywPP77yytWx9D879WSPTc/u6+92V/Rianpo7+R1K6MHP58vKkBuuummVx111FFTTdNMR0QMDQ1VOefzqqr6nf3nyDl3U0rtAaa56nbu5sRFjIFDtaR77NizfuXRKaXNSzknRdwYES/r0dxLusfOPfu0VZGXckZKee6TTr32nFdd2ovva0s/Z0p22TK06tRTb4je/Oy05HP2dVZM2WTLz8qB4/dGD/bDoj6Gd8WKFY8eGBg46pYbVdWVOecTpqamromI/he+8IUnLBh+WggJAADgABb7e0CGp6amXhwRUdd1X0Scm1L6yI4dO/blnD/R19c3FBExODh4ekrpl3LOH+7VggEAgOVrUQGSc35Z27ZPqev6soj4Rs75msnJyT+ZO/yiiDi3ruuvVFX1mYh4zcTExDd6tWAAAGD5OuB7QCYmJp608PLcLxz8+S1bthw7OTl50/x7QebG/ltE3H/Dhg2r9+7d+6OdO3fO9nbJAADAcnVQvzBw69atP7y1Yzt27Ljh0JcDAAAcyRb7HhAAAIBDJkAAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiuge6cmho6Je73e5l27Zt+/b8dRs3blzX19f3axFR5Zw/2DTNFfPHRkZG/kvbto/IOV+1YsWKv9q6detkgbUDAADLzP/3DMjIyMhZKaUdbdueseC6e3S73S/nnM/IOZ8eEV+s6/qMiIjh4eFNbdvuzDmvSCn9xuTk5CXr16/vFDwHAABgmfiJAKnr+ktt2344Io5feH3OeXNEvL9pmpc3TfOKnPP2nPNL5o79t6qqXjAxMfHf161b9+sRceLatWsfW+oEAACA5eMnAqRpmrOaplmbc/7YwutzzmflnD9+y42q6uMppbPOO++8NRFxatu2n4qIGB0dbSPikznnBxdYOwAAsMwc8D0g+0sprY6IG+Yvt217Q0pp9fT09Oqqqm5smmZ6wfAbIuK4A0xz0iLuajFjFu1TTxl6+Omrjj9rKeek9340te+qn/mLN/1lD6Y+cakn3HfVN1cfte4+Sz0tPZdTLPHjzZwl32Pv/PsrTjj3yacv9bQU8N6LrzwhIn6wxNMu+R6LiIicU6TUk6npnR9feeXqWPrHsp7ssenZff3d7opeTE0P7Z28bmX04PvlogIk59ymlG4ZW1VVN+fcVlU1e4A5OhHRHmCaq27nbk5cxJiD8vC7nvawiPSKpZyT3jvp6FWfjIgLejT9ku6xFSfd+4bbH8WdT8qxxHthgSWd99yzT1sVeSlnpJTnPunUa8951aW92GdLP2dKdtkytOrUU2+I3jyWLfmcfZ0VUzbZ8rNy4Pi90YP9sNiP4f1ezvnU+Qtt254aEd9buXLltRExsHnz5lveM5JSOjXn/L0lXicAAHAEWFSA5Jw/HBHPHx0drSIiRcQLIuLDb3zjG2+KiE9PT09viIgYHBw8Jef8Sznnj/ZqwQAAwPK1qABZsWLFWETs2b1797/Xdf21qqo6N91005siInLOWyJic13Xn62q6nM55zds3779671cNAAAsDwd8D0gExMTT1p4ee4XC/7aeeedt6aqquqCCy64bsHYL0fEPQcHB0856qijrvVLCAEAgDWcrhsAABexSURBVFuzqDehz9u2bdueWzu2ffv27x76cgAAgCPZYt+EDgAAcMgECAAAUIwAAQAAihEgAABAMQIEAAAoRoAAAADFCBAAAKAYAQIAABQjQAAAgGIECAAAUIwAAQAAihEgAABAMQIEAAAoRoAAAADFCBAAAKAYAQIAABQjQAAAgGIECAAAUIwAAQAAihEgAABAMQIEAAAoRoAAAADFCBAAAKAYAQIAABQjQAAAgGIECAAAUIwAAQAAihEgAABAMQIEAAAoRoAAAADFCBAAAKAYAQIAABQjQAAAgGIECAAAUIwAAQAAihEgAABAMQIEAAAoRoAAAADFCBAAAKAYAQIAABQjQAAAgGIECAAAUIwAAQAAihEgAABAMQIEAAAoRoAAAADFCBAAAKAYAQIAABQjQAAAgGIECAAAUIwAAQAAihEgAABAMQIEAAAoRoAAAADFCBAAAKAYAQIAABQjQAAAgGIECAAAUIwAAQAAihEgAABAMQIEAAAoRoAAAADFCBAAAKAYAQIAABQjQAAAgGIECAAAUIwAAQAAihEgAABAMQIEAAAoRoAAAADFCBAAAKAYAQIAABQjQAAAgGIECAAAUIwAAQAAihEgAABAMQIEAAAoRoAAAADFCBAAAKAYAQIAABQjQAAAgGIECAAAUIwAAQAAihEgAABAMQIEAAAoRoAAAADFCBAAAKAYAQIAABQjQAAAgGIECAAAUEx3sQOHhoYelFK6z/zltm2v3L59+2cjIuq6vktK6Vcioup0OhdfeOGF3+vBWgEAgGVu0c+ApJR+PyKeExFPiIgnVFX1sxERg4ODp0TEV3LOj8w5P2xmZuZfN23adO/eLBcAAFjOFv0MSEQ8MCIe0jTNDxZemVJ6UUT8TdM0IxERdV1Pdjqdl0bEi5ZumQAAwJFgUc+AbNy48Zi5sU+q63p0eHj4yfPHUkoPSSl9dMHwj6aUHrLE6wQAAI4Ai3oGpNPpPDAi7pZSOisirso5v7mu619omub3Ukpr2ra9YX5sznlPRKw5wDQnLeKuFjNm0a7dd+OqE1asXMopKWDf7Ex/LPFemHPiUk+476pvrj5q3X1ufyB3MjnFMtlj7/z7K04498mnL/W0FPDei688ISJ+cLsDD86S77GIiMg5RUo9mZre+fGVV66OpX8s68kem57d19/trujF1PTQ3snrVkYPvl8uKkD27dv3LytXrrzH2NjYNRERdV3/U0R8cf369X+Qc26rqurMj62qqptzbg8wzVW3czcnLmLMQTlhxdE/Xsr5KGNFpzsVS7wXFljSeVecdO8bbn8Udz4pxzLZY+eefdqqyEs5I6U890mnXnvOqy7txT5b+jlTssuWoVWnnnpD9OaxbMnn7OusmLLJlp+VA8fvjR7sh0W9BOvoo49eNzMzc8tTCevWrftmRPSvWbNmTUrp6rZtT14w/OSc89VLvVAAAGD5W+ynYD260+lsHx0d7UZE7N69++kR8a2mab6fc/5YSuk3IyJFRLRte25K6WO9WS4AALCcLeolWDnnt0fEr+7evfs/hoeHd+Wc75ZS+o25w9si4uy6rv81ImYj4saBgYE39mi9AADAMraoAGmaZjoinjUyMnLXmZmZldu3b//PBcdujIjHj4yMnNy2badpmit6tVgAAGB5O5jfAxLzb0K/lWO7Dn05AADAkWzRvwkdAADgUAkQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgGAECAAAUI0AAAIBiBAgAAFCMAAEAAIoRIAAAQDECBAAAKEaAAAAAxQgQAACgmO4SzZNGRkYeMzs7e2pK6XNN0/z7Es0LAAAcQZbkGZC6rt8zOzv7xxHxoIi4pK7rFyzFvAAAwJHlkANkZGTkrIh47PT09GMmJiZ+q23bcyLitRGRDnl1AADAEeWQAyTn/LCI+OSOHTv2RUSccsopn4qI40dGRtYd6twAAMCR5ZDfA5JzXptSun7+8ujoaFvX9Q0RsSYidi0YetIipnvQoa5noQfs/PO/OuW4Yz+4lHPSez+cnJyNJd4LC5y4lJP9+x8+6cqjf+YXnrKUc9J7abbNsUz22Lonfqh7zi+fbI8tQ+/5x12rojf7bEn3WETEV7duPafT7fpgmmXmhn/5l+tjmeyxt33sha9fcfTaP13qeemta35w2Y+iB3vskAMkpZRzzp39rq5mZ2fzftddtYjpFjNm0b72w6vjaz+8eimnZHk7KZZ4j/3oW1+OH33ry0s5Jcvbku+xq75/Y/zZuy5byilZ3pZ8j0VEXP4Xf+GBjHk92WOfumzHUk/JMrYU/9txTUTc8nKrLVu2DETE8Z1O59olmBsAADiCLEWAfDIiHjEyMnJyRMTk5OQzI+KbY2Nj1yzB3AAAwBHkkANkfHz8spTSG9u2/XRd138bEX+Sc96yBGsDAACOMEv2UbkjIyN3zTmf1Ol0vnXhhRf++A5M0ZPXHMIC9hi9Zo/Ra/YYvWaP8VNlMZ+SBYfCHqPX7DF6zR6j1+wxes5H7gEAAMXcmQLkxsO9AI549hi9Zo/Ra/YYvWaPAQAAAAAAAAAAAAAAAAAAAOUs2S8iZPE2bNiwemBg4Mzx8fGPH+61sLydd955a2ZmZh6y//Wzs7Nfe8tb3rL7QLdZv35957jjjnvM9u3bP9L7FXIkGx0drXbt2vX4brd75bZt2/5j4bEtW7YM7Nu371Hdbvfb27Zt+/ah3teWLVsGpqamft7jJgvVdX1GzvmYiYmJz93B2/dVVfWIsbGxjw0ODp4SEcds377960u8TJapoaGhB+ScTzzQ98uRkZGzZmZmVm3fvv0Th2Nty92d6WN4f2r09/f/bM75fYd7HSx/OeefSyn9TUqpXvinv7//vrd2m2OOOeboqqr+vuQ6OTJ95zvf6U8p/dPs7OzO/Y9NTU09Y+7Y85fivvbu3Xt8zvkDSzEXR5SdKaWLN2zYsPoO3v64tm3/OiIipfTUTqfzsiVcG8tcSukVVVV9eGho6GELr1+/fn1/27YXV1X1nsO1tuVOgNyJ1HXddyuH0ujo6EH9W61fv75zG7dJ4dmvI8l1TdM8e+GfsbGxjy0csH79+s6Bbji3R251Lyx2393G3l30HCxrA0NDQw9aeEXOeUNEfGP/gQezV25j33bv2DI5kgwODv5CSmkyIj7Y19f3GwcaczuPP2ndunXXN01zR+OFnw7fqKpqw8Ir1q5d+9SIuP5Ag2/rMY7/xw+hh0Fd14+MiJ1N05y0fv36/rVr147nnJ8aEVMRcWVEPK1pmqvqur4857w1pfTiiFgRERc1TfNf67p+XES8rGmap0ZEDA0N3Tel9J6maR5y/vnn321mZubdEfHAiIic88dSSr/Rtu39qqp6Q0R8JiJeHREfioj/0zTNmyIiBgcHH1VV1R81TfPYwl8ODsHIyMhj27Z9e9M0p+1/bP369Z01a9a8NiLOiYhjIuLd69ate8kVV1yxstvtXhsR2yPi1+LmvfXOdevWvTIi2t27d1+eUvrtnPO2tm2fVVXVu2666aZ7v+Md79gbEVHX9RdyzuecfPLJ39y9e/efRsTzI+KmiPhBSunXx8fHL6vr+t9SSq/JOf95zvlLKaWqaZpfnbt9X0T8+8zMzKNu7WViLA8bNmxY0d/ff1NEvDqldNfx8fGXRkQMDg6eUlXVJ3POH0opXd00zejw8PDP5ZzfGhHrIqIbEW9pmuZ3h4aGnp1SekREHBcRT9izZ889V69e/ScppfURMZtz/vyKFSuev3fv3lXdbvdfImJnRJwdEUflnF89MTExcXjOnsOtruuJnPNXq6q6LOf82qZpHhwRMTg4+JCqqi6IiKsj4qy4+Wed/9k0zf8aHh6+f875zTnnj6SUfi+l9JCc8webprnH0NDQ+VVVPWh8fLw+nOfFnUdd12+PiO9ExODAwMDpW7dunZy7/u8i4tMRcX7TNKfUdd2Xc96WUnp6REyllHallJ7Wtu2v5ZwfMzEx8RsRERs3bjym2+1+JSJ+rmmaHxym07pT8D+Th9nq1avXR8RJEXG3pmlOSil9NyKeMX84pXRC0zR3n56efkBEnHf++effLSL6ImLV/Bw5505EHBsRMTMz819TSp9tmuauAwMDp6SUHpxzflBVVd2IeHjO+e5VVZ2Rcx6LiBfOz9HpdJ6bc/6rMmfNEltT1/X4/J+hoaHfi4hYs2bNSETcZ8+ePfedmpo6OSJO27179/lztxmIiB80TXN6RPxMRDz6qquuGpw7dmLO+XkppUeecsopH4+INcccc8zC/6w4Nufc2bVr1+NTSo+ampo6uWmaU3LOH885P29uzHE555eklJ46PT39mxHx85s2bbp7RERK6Yk552+JjyNHVVXvzjk/a/5//qqqen5EvCulNDs/Juf8pymlP2ua5m7T09P3i4gt559//qq4eS8+PyK+0e12H7pmzZqXVFX1gIi417p1605LKd2wb9++V81Nc5eU0heaprl7SumJKaU/Lnyq3EnM7Z1n5pzfe9JJJ10cESfXdf3giIi573c/n1La2TTNabOzs4+MiN8dHh5+9Nz3y4dVVXXftm3vPzU1dW1EeAaE27InpfSJycnJp0ZE1HV9UkSc2bbtP84PSCk9I6V0j5j7WS7n/O3Z2dln9fX1fSCldPamTZvWRkT09fU9LSI++9MeHxEC5LCbmJh4V39//9Mi4sFDQ0NDOecz4+b/rY6IiNnZ2QsjIr/1rW+9NiK+Ozs7e+Jtzdc0zUtyzn88ODj4uKmpqc0Rcdec8/x8nenp6ReNjY195+STT/6niFg1MjLys6Ojo92c81P7+/vf2aPTpLemU0pfmP9TVdXX5q5fHxG71q5du2FgYOD5EXFFRDxr7lh70003vT4i8twD4Ztyzk+fO9ZXVdUrx8fHvzY6Ojpza3c6MTFxSc75F7vd7gPqun5BSunh8ZNh/Ibx8fHP79ix44aIeFen0zknIqJt2+dUVfXWpf0ScDhVVfX9lNKlcfMzEykiXpBzftvCMU3TPGF2dvafhoeHf6Xb7b44IlZMTU2tnDt8RdM0r7/wwgu/FxFPb9v2jU3T3Dg6Otru2bOn7uvre+3cuBvHx8ffEhExPj7+LxFx7JYtWwbKnCV3JrOzs8+JiO+mlB64a9eux6aUvhQRgwuG/Of4+Pg7IyIuuuiiyyNix4LHuP6c8+bt27f/Z19fXy68dJahtm13zL2sNCLieRHx3oX/wTI+Pv6+gYGBs6uqOquu68GIeFBVVcdccMEF10XE33W73fnvvc+Zeyb4p57X0RawYcOG1X19fa88+eSTXzU6OtouPDY8PPxLk5OT2yLib6uq+rec82cWHl+xYsWPF15u2zal9JOvnOt2u522befn25Jzrjud/9ve/cXGUdxxAP9+53xxXIu6LjRVnTSK4IEADw1JJRBq06S0qA/0z0OLoSDFXLJz51ZBoISSPvVKS6o+UKJAXe/u2bgKiMCpIFJBcAAJ2gqoBIjESSqVSEbISaREcoz8d2+98+uD95KLeyBa4T/g30eytLMzO56xxne++c2MM087544ZY2pPphnq6+ubAoBiseistT1Jktxx+vTplwG8nv6iqE+fMd/3gzr3WwBc5py7PE2PG2MOpNfxvn37JqoFSY6ISEs1PTU1deIjvl8GANLZxidJHhSRAZIXnU5kjDlfh3MuNMY83tHRsYfkxiiK8v9bF9Vi55zrA5ArFApnkyQ5G4bhv629sJLFWvswya875w4A+CeAkZrH3625/kImkzlXTZTL5QqASi6X+zyAcQAX/cE4OjqqS4mXIBHZKiKTJO9L01kAP7XW7kyLfDCr/AjJtjR5KgiCCSj1MY2MjBxqbW3tTqMfW0j+RESWVfMLhcKmKIpCETkA4BjJ16p5xpjQOffrzs7OcpIka9MJ4CVPIyDzwBiTkLxvaGioDQBE5EsARtPrPMmHgyDY6fv+owA+9zGqnEBNlERE1tdc7wCwzff9X5F8RkS+WPPc7NnsR0n+WERuI9n7f3ZPLV5vknwrDMNdYRjuItkvIpU0r9HzvI3Vgs65GwG8U02vWbOmdqxMII1sbNu27csAVqX37xSR/WEY3pWuw8+gZl9ZkiTn6yiVSkdIji1btmwXgOeqH4TVZwfJgyQ3OOd+AeCi6Ecul7sEwM/iOL4pDMPfkXwDQHNNkdrxdjRJkvNj01p7j7W2Z04brz5VPM+7BsDaOI6/FQTBd9OvTQBOYSbyCwBXpcfqAgBIfhsXXuM+NLKrVD3lcjkRkSdEZK+IjPm+f7w2P0kSC6ArDMMdYRj2kmyq5nV3d78KYIVz7m4AT8yeiF6qNAIyD3p7e0c9z3uW5H5r7YsAbseFN+gBESnk8/kWEbkWwFUALt2+fXtXFEV162toaHhnenp6lbW2W0TGnXPrq1ERkgPOud94nvcPEdlMEsaYdgD+7HrSje6HAWweHh725qDragE553YbY16y1l4J4CyAO0jelmZPk/yDtfZlAJcC+I5zblO9ekTkhenp6f3W2lcAbAZwGpgZawB+aa2tAFgL4GsAonSGqF57QpI+gOs+sU6qRSMIgtjzvKdIFkhedPRub2/vmLV2MJvN/snzvEEAmwB8YIxpF5FztWWNMfc75/rz+fwq59wUgHaSN81fT9RiR3KriDw5eyJDRPYZY7aKyL2YGV9/tdY+LyLXAPjK5ORkT1NT0+X1a1Xqo4nIn40xx0Xk53WyBwDYfD7fLCLrRORqAG0dHR2P9PX1jYhID4Ddzrkr57fVi5dGQOZJeqLLXpKjJDuDIHgAANra2h4QkZ0ATgIoAtiYRiMiAFvOnDkzWq1DRHbEcfxeV1fXWENDw7UkXyP5BskfichdaZl2Y0wPyRMkt4jI90j2ZzKZQQA76jTtiIg8Vi6Xkzp5apGLougYgM56eaVSabBSqWwg2Q/gXefcN3zff3316tWTJNsrlcqNJI+QPJjJZNaVSqXBYrEoAG4pFovnxwPJLST3isj7AG4Vka1NTU0nfd8PAeRE5KSI7GlsbLyB5EMAJkSkEEXRmdr2iMhhAEeDIHh7zn4gal6Nj4/HAG5ZsWLFFAAkSfIggJurGyyNMT0iUsbMsqnrARwieSRJkh86574PYMA59zfn3J5qnd3d3UfjOF7nnHsVwNvGmPW+7x9vaWk5JyK1a/xB8ta0DWoJIXkwm83urnM/xMz7rAEwZIy5meQJAKXm5uYb0pP83id5d/WZxsbGUQAd6fP9SZL812SdWrqMMY+IyHMAUCqV/kXyB8uXL38MAOI4fk9ECgCwcuXK35O8B8BJ59z92Wz2myIStLa2Rmk9h0n+vVQqDS5YZxYZXTu7ROVyuUsymcwVJJ9OkmRzuklPqTlhrV0L4LckD33IfhWllPpEWGuvA9AVBMGGhW6LWvLoed7VAB4yxgS+7+s/U01pBGSJymazXzXG7CVZ1A8faq6JyIMicmp4eFjX8iul5hTJYQAvLXQ7lAIAkn8k+Zbv+39Z6LYopZRSSimllFJKKaWUUkoppZRSSimllPpM+A9asy+NVX+xFQAAAABJRU5ErkJggg==" alt="Chart" />
|
|
13
|
+
|
|
14
|
+
<h2>Conclusion</h2>
|
|
15
|
+
<p>This is the end of our report.</p>
|
|
16
|
+
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|
|
19
|
+
|
package/test-report.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"document": "# Sales Report\n\nThis is our monthly sales report.\n\n[[chart:sales]]\n\n## Conclusion\n\nThis is the end of our report.",
|
|
3
|
+
"charts": {
|
|
4
|
+
"sales": {
|
|
5
|
+
"type": "bar",
|
|
6
|
+
"config": {
|
|
7
|
+
"labels": ["January", "February", "March", "April", "May"],
|
|
8
|
+
"datasets": [
|
|
9
|
+
{
|
|
10
|
+
"label": "Sales",
|
|
11
|
+
"data": [100, 150, 200, 175, 225],
|
|
12
|
+
"backgroundColor": ["#FF6384", "#36A2EB", "#FFCE56", "#4BC0C0", "#9966FF"]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"options": {
|
|
16
|
+
"title": "Monthly Sales Data"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"outputFile": "test-report.html"
|
|
22
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true
|
|
10
|
+
},
|
|
11
|
+
"include": ["src/**/*"],
|
|
12
|
+
"exclude": ["node_modules"]
|
|
13
|
+
}
|