euparliamentmonitor 0.8.4
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/LICENSE +201 -0
- package/README.md +1005 -0
- package/SECURITY.md +151 -0
- package/package.json +131 -0
- package/scripts/constants/committee-indicator-map.d.ts +199 -0
- package/scripts/constants/committee-indicator-map.d.ts.map +1 -0
- package/scripts/constants/committee-indicator-map.js +1224 -0
- package/scripts/constants/committee-indicator-map.js.map +1 -0
- package/scripts/constants/config.d.ts +38 -0
- package/scripts/constants/config.d.ts.map +1 -0
- package/scripts/constants/config.js +66 -0
- package/scripts/constants/config.js.map +1 -0
- package/scripts/constants/language-articles.d.ts +84 -0
- package/scripts/constants/language-articles.d.ts.map +1 -0
- package/scripts/constants/language-articles.js +6771 -0
- package/scripts/constants/language-articles.js.map +1 -0
- package/scripts/constants/language-core.d.ts +38 -0
- package/scripts/constants/language-core.d.ts.map +1 -0
- package/scripts/constants/language-core.js +90 -0
- package/scripts/constants/language-core.js.map +1 -0
- package/scripts/constants/language-ui.d.ts +82 -0
- package/scripts/constants/language-ui.d.ts.map +1 -0
- package/scripts/constants/language-ui.js +889 -0
- package/scripts/constants/language-ui.js.map +1 -0
- package/scripts/constants/languages.d.ts +14 -0
- package/scripts/constants/languages.d.ts.map +1 -0
- package/scripts/constants/languages.js +15 -0
- package/scripts/constants/languages.js.map +1 -0
- package/scripts/generators/analysis-builders.d.ts +266 -0
- package/scripts/generators/analysis-builders.d.ts.map +1 -0
- package/scripts/generators/analysis-builders.js +2903 -0
- package/scripts/generators/analysis-builders.js.map +1 -0
- package/scripts/generators/breaking-content.d.ts +45 -0
- package/scripts/generators/breaking-content.d.ts.map +1 -0
- package/scripts/generators/breaking-content.js +530 -0
- package/scripts/generators/breaking-content.js.map +1 -0
- package/scripts/generators/committee-helpers.d.ts +54 -0
- package/scripts/generators/committee-helpers.d.ts.map +1 -0
- package/scripts/generators/committee-helpers.js +154 -0
- package/scripts/generators/committee-helpers.js.map +1 -0
- package/scripts/generators/dashboard-content.d.ts +95 -0
- package/scripts/generators/dashboard-content.d.ts.map +1 -0
- package/scripts/generators/dashboard-content.js +630 -0
- package/scripts/generators/dashboard-content.js.map +1 -0
- package/scripts/generators/deep-analysis-content.d.ts +23 -0
- package/scripts/generators/deep-analysis-content.d.ts.map +1 -0
- package/scripts/generators/deep-analysis-content.js +831 -0
- package/scripts/generators/deep-analysis-content.js.map +1 -0
- package/scripts/generators/mindmap-content.d.ts +55 -0
- package/scripts/generators/mindmap-content.d.ts.map +1 -0
- package/scripts/generators/mindmap-content.js +512 -0
- package/scripts/generators/mindmap-content.js.map +1 -0
- package/scripts/generators/motions-content.d.ts +50 -0
- package/scripts/generators/motions-content.d.ts.map +1 -0
- package/scripts/generators/motions-content.js +391 -0
- package/scripts/generators/motions-content.js.map +1 -0
- package/scripts/generators/news-enhanced.d.ts +14 -0
- package/scripts/generators/news-enhanced.d.ts.map +1 -0
- package/scripts/generators/news-enhanced.js +169 -0
- package/scripts/generators/news-enhanced.js.map +1 -0
- package/scripts/generators/news-indexes.d.ts +31 -0
- package/scripts/generators/news-indexes.d.ts.map +1 -0
- package/scripts/generators/news-indexes.js +410 -0
- package/scripts/generators/news-indexes.js.map +1 -0
- package/scripts/generators/pipeline/fetch-stage.d.ts +352 -0
- package/scripts/generators/pipeline/fetch-stage.d.ts.map +1 -0
- package/scripts/generators/pipeline/fetch-stage.js +1522 -0
- package/scripts/generators/pipeline/fetch-stage.js.map +1 -0
- package/scripts/generators/pipeline/generate-stage.d.ts +43 -0
- package/scripts/generators/pipeline/generate-stage.d.ts.map +1 -0
- package/scripts/generators/pipeline/generate-stage.js +204 -0
- package/scripts/generators/pipeline/generate-stage.js.map +1 -0
- package/scripts/generators/pipeline/output-stage.d.ts +48 -0
- package/scripts/generators/pipeline/output-stage.d.ts.map +1 -0
- package/scripts/generators/pipeline/output-stage.js +145 -0
- package/scripts/generators/pipeline/output-stage.js.map +1 -0
- package/scripts/generators/pipeline/transform-stage.d.ts +57 -0
- package/scripts/generators/pipeline/transform-stage.d.ts.map +1 -0
- package/scripts/generators/pipeline/transform-stage.js +111 -0
- package/scripts/generators/pipeline/transform-stage.js.map +1 -0
- package/scripts/generators/propositions-content.d.ts +29 -0
- package/scripts/generators/propositions-content.d.ts.map +1 -0
- package/scripts/generators/propositions-content.js +90 -0
- package/scripts/generators/propositions-content.js.map +1 -0
- package/scripts/generators/sankey-content.d.ts +45 -0
- package/scripts/generators/sankey-content.d.ts.map +1 -0
- package/scripts/generators/sankey-content.js +227 -0
- package/scripts/generators/sankey-content.js.map +1 -0
- package/scripts/generators/sitemap.d.ts +66 -0
- package/scripts/generators/sitemap.d.ts.map +1 -0
- package/scripts/generators/sitemap.js +562 -0
- package/scripts/generators/sitemap.js.map +1 -0
- package/scripts/generators/strategies/article-strategy.d.ts +146 -0
- package/scripts/generators/strategies/article-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/article-strategy.js +4 -0
- package/scripts/generators/strategies/article-strategy.js.map +1 -0
- package/scripts/generators/strategies/breaking-news-strategy.d.ts +64 -0
- package/scripts/generators/strategies/breaking-news-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/breaking-news-strategy.js +246 -0
- package/scripts/generators/strategies/breaking-news-strategy.js.map +1 -0
- package/scripts/generators/strategies/committee-reports-strategy.d.ts +93 -0
- package/scripts/generators/strategies/committee-reports-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/committee-reports-strategy.js +447 -0
- package/scripts/generators/strategies/committee-reports-strategy.js.map +1 -0
- package/scripts/generators/strategies/month-ahead-strategy.d.ts +60 -0
- package/scripts/generators/strategies/month-ahead-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/month-ahead-strategy.js +175 -0
- package/scripts/generators/strategies/month-ahead-strategy.js.map +1 -0
- package/scripts/generators/strategies/monthly-review-strategy.d.ts +66 -0
- package/scripts/generators/strategies/monthly-review-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/monthly-review-strategy.js +204 -0
- package/scripts/generators/strategies/monthly-review-strategy.js.map +1 -0
- package/scripts/generators/strategies/motions-strategy.d.ts +61 -0
- package/scripts/generators/strategies/motions-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/motions-strategy.js +215 -0
- package/scripts/generators/strategies/motions-strategy.js.map +1 -0
- package/scripts/generators/strategies/propositions-strategy.d.ts +60 -0
- package/scripts/generators/strategies/propositions-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/propositions-strategy.js +257 -0
- package/scripts/generators/strategies/propositions-strategy.js.map +1 -0
- package/scripts/generators/strategies/week-ahead-strategy.d.ts +57 -0
- package/scripts/generators/strategies/week-ahead-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/week-ahead-strategy.js +178 -0
- package/scripts/generators/strategies/week-ahead-strategy.js.map +1 -0
- package/scripts/generators/strategies/weekly-review-strategy.d.ts +63 -0
- package/scripts/generators/strategies/weekly-review-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/weekly-review-strategy.js +211 -0
- package/scripts/generators/strategies/weekly-review-strategy.js.map +1 -0
- package/scripts/generators/swot-content.d.ts +42 -0
- package/scripts/generators/swot-content.d.ts.map +1 -0
- package/scripts/generators/swot-content.js +366 -0
- package/scripts/generators/swot-content.js.map +1 -0
- package/scripts/generators/week-ahead-content.d.ts +103 -0
- package/scripts/generators/week-ahead-content.d.ts.map +1 -0
- package/scripts/generators/week-ahead-content.js +610 -0
- package/scripts/generators/week-ahead-content.js.map +1 -0
- package/scripts/index.d.ts +40 -0
- package/scripts/index.d.ts.map +1 -0
- package/scripts/index.js +53 -0
- package/scripts/index.js.map +1 -0
- package/scripts/mcp/ep-mcp-client.d.ts +471 -0
- package/scripts/mcp/ep-mcp-client.d.ts.map +1 -0
- package/scripts/mcp/ep-mcp-client.js +734 -0
- package/scripts/mcp/ep-mcp-client.js.map +1 -0
- package/scripts/mcp/mcp-connection.d.ts +264 -0
- package/scripts/mcp/mcp-connection.d.ts.map +1 -0
- package/scripts/mcp/mcp-connection.js +790 -0
- package/scripts/mcp/mcp-connection.js.map +1 -0
- package/scripts/mcp/mcp-health.d.ts +75 -0
- package/scripts/mcp/mcp-health.d.ts.map +1 -0
- package/scripts/mcp/mcp-health.js +78 -0
- package/scripts/mcp/mcp-health.js.map +1 -0
- package/scripts/mcp/mcp-retry.d.ts +94 -0
- package/scripts/mcp/mcp-retry.d.ts.map +1 -0
- package/scripts/mcp/mcp-retry.js +127 -0
- package/scripts/mcp/mcp-retry.js.map +1 -0
- package/scripts/mcp/wb-mcp-client.d.ts +38 -0
- package/scripts/mcp/wb-mcp-client.d.ts.map +1 -0
- package/scripts/mcp/wb-mcp-client.js +112 -0
- package/scripts/mcp/wb-mcp-client.js.map +1 -0
- package/scripts/templates/article-template.d.ts +9 -0
- package/scripts/templates/article-template.d.ts.map +1 -0
- package/scripts/templates/article-template.js +378 -0
- package/scripts/templates/article-template.js.map +1 -0
- package/scripts/templates/section-builders.d.ts +28 -0
- package/scripts/templates/section-builders.d.ts.map +1 -0
- package/scripts/templates/section-builders.js +142 -0
- package/scripts/templates/section-builders.js.map +1 -0
- package/scripts/types/analysis.d.ts +115 -0
- package/scripts/types/analysis.d.ts.map +1 -0
- package/scripts/types/analysis.js +4 -0
- package/scripts/types/analysis.js.map +1 -0
- package/scripts/types/common.d.ts +584 -0
- package/scripts/types/common.d.ts.map +1 -0
- package/scripts/types/common.js +96 -0
- package/scripts/types/common.js.map +1 -0
- package/scripts/types/generation.d.ts +104 -0
- package/scripts/types/generation.d.ts.map +1 -0
- package/scripts/types/generation.js +4 -0
- package/scripts/types/generation.js.map +1 -0
- package/scripts/types/index.d.ts +24 -0
- package/scripts/types/index.d.ts.map +1 -0
- package/scripts/types/index.js +16 -0
- package/scripts/types/index.js.map +1 -0
- package/scripts/types/intelligence.d.ts +129 -0
- package/scripts/types/intelligence.d.ts.map +1 -0
- package/scripts/types/intelligence.js +4 -0
- package/scripts/types/intelligence.js.map +1 -0
- package/scripts/types/mcp.d.ts +418 -0
- package/scripts/types/mcp.d.ts.map +1 -0
- package/scripts/types/mcp.js +4 -0
- package/scripts/types/mcp.js.map +1 -0
- package/scripts/types/parliament.d.ts +388 -0
- package/scripts/types/parliament.d.ts.map +1 -0
- package/scripts/types/parliament.js +4 -0
- package/scripts/types/parliament.js.map +1 -0
- package/scripts/types/quality.d.ts +114 -0
- package/scripts/types/quality.d.ts.map +1 -0
- package/scripts/types/quality.js +4 -0
- package/scripts/types/quality.js.map +1 -0
- package/scripts/types/stakeholder.d.ts +88 -0
- package/scripts/types/stakeholder.d.ts.map +1 -0
- package/scripts/types/stakeholder.js +16 -0
- package/scripts/types/stakeholder.js.map +1 -0
- package/scripts/types/visualization.d.ts +708 -0
- package/scripts/types/visualization.d.ts.map +1 -0
- package/scripts/types/visualization.js +4 -0
- package/scripts/types/visualization.js.map +1 -0
- package/scripts/types/world-bank.d.ts +85 -0
- package/scripts/types/world-bank.d.ts.map +1 -0
- package/scripts/types/world-bank.js +4 -0
- package/scripts/types/world-bank.js.map +1 -0
- package/scripts/utils/article-category.d.ts +18 -0
- package/scripts/utils/article-category.d.ts.map +1 -0
- package/scripts/utils/article-category.js +49 -0
- package/scripts/utils/article-category.js.map +1 -0
- package/scripts/utils/article-quality-scorer.d.ts +87 -0
- package/scripts/utils/article-quality-scorer.d.ts.map +1 -0
- package/scripts/utils/article-quality-scorer.js +1048 -0
- package/scripts/utils/article-quality-scorer.js.map +1 -0
- package/scripts/utils/content-metadata.d.ts +34 -0
- package/scripts/utils/content-metadata.d.ts.map +1 -0
- package/scripts/utils/content-metadata.js +249 -0
- package/scripts/utils/content-metadata.js.map +1 -0
- package/scripts/utils/content-validator.d.ts +94 -0
- package/scripts/utils/content-validator.d.ts.map +1 -0
- package/scripts/utils/content-validator.js +489 -0
- package/scripts/utils/content-validator.js.map +1 -0
- package/scripts/utils/copy-test-reports.d.ts +9 -0
- package/scripts/utils/copy-test-reports.d.ts.map +1 -0
- package/scripts/utils/copy-test-reports.js +508 -0
- package/scripts/utils/copy-test-reports.js.map +1 -0
- package/scripts/utils/file-utils.d.ts +144 -0
- package/scripts/utils/file-utils.d.ts.map +1 -0
- package/scripts/utils/file-utils.js +374 -0
- package/scripts/utils/file-utils.js.map +1 -0
- package/scripts/utils/fix-articles.d.ts +27 -0
- package/scripts/utils/fix-articles.d.ts.map +1 -0
- package/scripts/utils/fix-articles.js +510 -0
- package/scripts/utils/fix-articles.js.map +1 -0
- package/scripts/utils/generate-docs-index.d.ts +8 -0
- package/scripts/utils/generate-docs-index.d.ts.map +1 -0
- package/scripts/utils/generate-docs-index.js +275 -0
- package/scripts/utils/generate-docs-index.js.map +1 -0
- package/scripts/utils/html-sanitize.d.ts +18 -0
- package/scripts/utils/html-sanitize.d.ts.map +1 -0
- package/scripts/utils/html-sanitize.js +57 -0
- package/scripts/utils/html-sanitize.js.map +1 -0
- package/scripts/utils/intelligence-analysis.d.ts +173 -0
- package/scripts/utils/intelligence-analysis.d.ts.map +1 -0
- package/scripts/utils/intelligence-analysis.js +936 -0
- package/scripts/utils/intelligence-analysis.js.map +1 -0
- package/scripts/utils/intelligence-index.d.ts +126 -0
- package/scripts/utils/intelligence-index.d.ts.map +1 -0
- package/scripts/utils/intelligence-index.js +731 -0
- package/scripts/utils/intelligence-index.js.map +1 -0
- package/scripts/utils/metadata-utils.d.ts +14 -0
- package/scripts/utils/metadata-utils.d.ts.map +1 -0
- package/scripts/utils/metadata-utils.js +18 -0
- package/scripts/utils/metadata-utils.js.map +1 -0
- package/scripts/utils/news-metadata.d.ts +47 -0
- package/scripts/utils/news-metadata.d.ts.map +1 -0
- package/scripts/utils/news-metadata.js +259 -0
- package/scripts/utils/news-metadata.js.map +1 -0
- package/scripts/utils/validate-articles.d.ts +2 -0
- package/scripts/utils/validate-articles.d.ts.map +1 -0
- package/scripts/utils/validate-articles.js +284 -0
- package/scripts/utils/validate-articles.js.map +1 -0
- package/scripts/utils/validate-ep-api.d.ts +51 -0
- package/scripts/utils/validate-ep-api.d.ts.map +1 -0
- package/scripts/utils/validate-ep-api.js +160 -0
- package/scripts/utils/validate-ep-api.js.map +1 -0
- package/scripts/utils/world-bank-data.d.ts +84 -0
- package/scripts/utils/world-bank-data.d.ts.map +1 -0
- package/scripts/utils/world-bank-data.js +311 -0
- package/scripts/utils/world-bank-data.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform-stage.d.ts","sourceRoot":"","sources":["../../../src/generators/pipeline/transform-stage.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AAIH,0CAA0C;AAC1C,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,KAAK,EAAE,OAAO,CAAC;IACf,8EAA8E;IAC9E,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAqBD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,gBAAgB,CAkCrF;AAID;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASzD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024-2026 Hack23 AB
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Validate the first item of a content array is a non-null object with a
|
|
5
|
+
* `text` string field. Returns an error description or null when valid.
|
|
6
|
+
*
|
|
7
|
+
* @param item - First element of the content array
|
|
8
|
+
* @returns Error message or null
|
|
9
|
+
*/
|
|
10
|
+
function validateFirstContentItem(item) {
|
|
11
|
+
if (item === null || item === undefined || typeof item !== 'object' || Array.isArray(item)) {
|
|
12
|
+
return "First content item is missing a 'text' string field";
|
|
13
|
+
}
|
|
14
|
+
const obj = item;
|
|
15
|
+
return typeof obj['text'] === 'string'
|
|
16
|
+
? null
|
|
17
|
+
: "First content item is missing a 'text' string field";
|
|
18
|
+
}
|
|
19
|
+
// ─── MCP Response validation ──────────────────────────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Validate that an MCP tool response has the standard envelope shape:
|
|
22
|
+
* `{ content: [{ text: string }] }`.
|
|
23
|
+
*
|
|
24
|
+
* Does **not** throw — returns a {@link ValidationResult} so callers can
|
|
25
|
+
* choose their own degradation strategy.
|
|
26
|
+
*
|
|
27
|
+
* @param toolName - Tool name used in warning messages
|
|
28
|
+
* @param data - Raw response to validate
|
|
29
|
+
* @returns Validation result with any error descriptions
|
|
30
|
+
*/
|
|
31
|
+
export function validateMCPResponse(toolName, data) {
|
|
32
|
+
const errors = [];
|
|
33
|
+
if (data === null || data === undefined) {
|
|
34
|
+
errors.push('Response is null or undefined');
|
|
35
|
+
console.warn(`⚠️ MCP validation failed for ${toolName}: response is null/undefined`);
|
|
36
|
+
return { valid: false, errors };
|
|
37
|
+
}
|
|
38
|
+
if (typeof data !== 'object' || Array.isArray(data)) {
|
|
39
|
+
errors.push(`Expected object, got ${Array.isArray(data) ? 'array' : typeof data}`);
|
|
40
|
+
console.warn(`⚠️ MCP validation failed for ${toolName}:`, errors.join(', '));
|
|
41
|
+
return { valid: false, errors };
|
|
42
|
+
}
|
|
43
|
+
const response = data;
|
|
44
|
+
if (!Array.isArray(response['content'])) {
|
|
45
|
+
errors.push("Missing or invalid 'content' array");
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const content = response['content'];
|
|
49
|
+
if (content.length === 0) {
|
|
50
|
+
errors.push('Empty content array');
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const itemError = validateFirstContentItem(content[0]);
|
|
54
|
+
if (itemError)
|
|
55
|
+
errors.push(itemError);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (errors.length > 0) {
|
|
59
|
+
console.warn(`⚠️ MCP validation failed for ${toolName}:`, errors.join(', '));
|
|
60
|
+
}
|
|
61
|
+
return { valid: errors.length === 0, errors };
|
|
62
|
+
}
|
|
63
|
+
// ─── Data normalisation helpers ───────────────────────────────────────────────
|
|
64
|
+
/**
|
|
65
|
+
* Normalise a date string to YYYY-MM-DD ISO 8601 format.
|
|
66
|
+
* Returns the input unchanged when parsing fails.
|
|
67
|
+
*
|
|
68
|
+
* @param date - Raw date string
|
|
69
|
+
* @returns Normalised ISO 8601 date string
|
|
70
|
+
*/
|
|
71
|
+
export function normalizeISO8601Date(date) {
|
|
72
|
+
if (!date)
|
|
73
|
+
return date;
|
|
74
|
+
try {
|
|
75
|
+
const parsed = new Date(date);
|
|
76
|
+
if (isNaN(parsed.getTime()))
|
|
77
|
+
return date;
|
|
78
|
+
return parsed.toISOString().split('T')[0] ?? date;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return date;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Sanitise a text string by collapsing internal whitespace and trimming edges.
|
|
86
|
+
*
|
|
87
|
+
* @param text - Raw text string
|
|
88
|
+
* @returns Sanitised text string
|
|
89
|
+
*/
|
|
90
|
+
export function sanitizeText(text) {
|
|
91
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Validate that a country code matches the ISO 3166-1 alpha-2 pattern.
|
|
95
|
+
*
|
|
96
|
+
* @param code - Two-letter country code
|
|
97
|
+
* @returns `true` when the code matches `[A-Z]{2}`
|
|
98
|
+
*/
|
|
99
|
+
export function isValidCountryCode(code) {
|
|
100
|
+
return /^[A-Z]{2}$/.test(code);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Validate that a language code matches the ISO 639-1 alpha-2 pattern.
|
|
104
|
+
*
|
|
105
|
+
* @param code - Two-letter language code
|
|
106
|
+
* @returns `true` when the code matches `[a-z]{2}`
|
|
107
|
+
*/
|
|
108
|
+
export function isValidLanguageCode(code) {
|
|
109
|
+
return /^[a-z]{2}$/.test(code);
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=transform-stage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform-stage.js","sourceRoot":"","sources":["../../../src/generators/pipeline/transform-stage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PropositionsStrings } from '../types/index.js';
|
|
2
|
+
/** Structured pipeline data returned from MCP before language-specific rendering */
|
|
3
|
+
export interface PipelineData {
|
|
4
|
+
healthScore: number;
|
|
5
|
+
throughput: number;
|
|
6
|
+
procRowsHtml: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Build propositions article HTML content with localized strings.
|
|
10
|
+
*
|
|
11
|
+
* **Security contract**: `proposalsHtml`, `adoptedTextsHtml`, `procedureHtml`,
|
|
12
|
+
* and `pipelineData.procRowsHtml` MUST be pre-sanitized HTML — all external
|
|
13
|
+
* (MCP-sourced) values must have been passed through `escapeHTML()` before
|
|
14
|
+
* being interpolated into these strings. The fetch helpers
|
|
15
|
+
* (`fetchProposalsFromMCP`, `fetchPipelineFromMCP`,
|
|
16
|
+
* `fetchProcedureStatusFromMCP`) fulfil this contract; callers must do the
|
|
17
|
+
* same if they construct these arguments independently.
|
|
18
|
+
*
|
|
19
|
+
* @param proposalsHtml - Pre-sanitized HTML for legislative procedures list section
|
|
20
|
+
* @param adoptedTextsHtml - Pre-sanitized HTML for recently adopted texts section (may be empty)
|
|
21
|
+
* @param pipelineData - Structured pipeline data from MCP (null when unavailable);
|
|
22
|
+
* `pipelineData.procRowsHtml` must be pre-sanitized HTML
|
|
23
|
+
* @param procedureHtml - Pre-sanitized HTML for tracked procedure status section (may be empty)
|
|
24
|
+
* @param strings - Localized string set for the target language
|
|
25
|
+
* @param lang - Language code for editorial string headings (default: 'en')
|
|
26
|
+
* @returns Full article HTML content string
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildPropositionsContent(proposalsHtml: string, adoptedTextsHtml: string, pipelineData: PipelineData | null, procedureHtml: string, strings: PropositionsStrings, lang?: string): string;
|
|
29
|
+
//# sourceMappingURL=propositions-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"propositions-content.d.ts","sourceRoot":"","sources":["../../src/generators/propositions-content.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,MAAM,EACrB,gBAAgB,EAAE,MAAM,EACxB,YAAY,EAAE,YAAY,GAAG,IAAI,EACjC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,mBAAmB,EAC5B,IAAI,SAAO,GACV,MAAM,CA2DR"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024-2026 Hack23 AB
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* @module Generators/PropositionsContent
|
|
5
|
+
* @description Pure function for building propositions article HTML with
|
|
6
|
+
* localized strings and pre-sanitized MCP data.
|
|
7
|
+
*/
|
|
8
|
+
import { escapeHTML } from '../utils/file-utils.js';
|
|
9
|
+
import { getLocalizedString, EDITORIAL_STRINGS } from '../constants/languages.js';
|
|
10
|
+
/**
|
|
11
|
+
* Build propositions article HTML content with localized strings.
|
|
12
|
+
*
|
|
13
|
+
* **Security contract**: `proposalsHtml`, `adoptedTextsHtml`, `procedureHtml`,
|
|
14
|
+
* and `pipelineData.procRowsHtml` MUST be pre-sanitized HTML — all external
|
|
15
|
+
* (MCP-sourced) values must have been passed through `escapeHTML()` before
|
|
16
|
+
* being interpolated into these strings. The fetch helpers
|
|
17
|
+
* (`fetchProposalsFromMCP`, `fetchPipelineFromMCP`,
|
|
18
|
+
* `fetchProcedureStatusFromMCP`) fulfil this contract; callers must do the
|
|
19
|
+
* same if they construct these arguments independently.
|
|
20
|
+
*
|
|
21
|
+
* @param proposalsHtml - Pre-sanitized HTML for legislative procedures list section
|
|
22
|
+
* @param adoptedTextsHtml - Pre-sanitized HTML for recently adopted texts section (may be empty)
|
|
23
|
+
* @param pipelineData - Structured pipeline data from MCP (null when unavailable);
|
|
24
|
+
* `pipelineData.procRowsHtml` must be pre-sanitized HTML
|
|
25
|
+
* @param procedureHtml - Pre-sanitized HTML for tracked procedure status section (may be empty)
|
|
26
|
+
* @param strings - Localized string set for the target language
|
|
27
|
+
* @param lang - Language code for editorial string headings (default: 'en')
|
|
28
|
+
* @returns Full article HTML content string
|
|
29
|
+
*/
|
|
30
|
+
export function buildPropositionsContent(proposalsHtml, adoptedTextsHtml, pipelineData, procedureHtml, strings, lang = 'en') {
|
|
31
|
+
const editorial = getLocalizedString(EDITORIAL_STRINGS, lang);
|
|
32
|
+
const pipelineHtml = pipelineData
|
|
33
|
+
? `
|
|
34
|
+
<div class="pipeline-metrics">
|
|
35
|
+
<div class="metric" aria-label="${escapeHTML(strings.pipelineHealthLabel)}">
|
|
36
|
+
<span class="metric-label">${escapeHTML(strings.pipelineHealthLabel)}</span>
|
|
37
|
+
<span class="metric-value">${escapeHTML(String(Math.round(pipelineData.healthScore * 100)))}%</span>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="metric" aria-label="${escapeHTML(strings.throughputRateLabel)}">
|
|
40
|
+
<span class="metric-label">${escapeHTML(strings.throughputRateLabel)}</span>
|
|
41
|
+
<span class="metric-value">${escapeHTML(String(pipelineData.throughput))}</span>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
${pipelineData.procRowsHtml}`
|
|
45
|
+
: '';
|
|
46
|
+
const procedureSection = procedureHtml
|
|
47
|
+
? `
|
|
48
|
+
<section class="procedure-status">
|
|
49
|
+
<h2>${escapeHTML(strings.procedureHeading)}</h2>
|
|
50
|
+
${procedureHtml}
|
|
51
|
+
</section>`
|
|
52
|
+
: '';
|
|
53
|
+
const adoptedTextsSection = adoptedTextsHtml
|
|
54
|
+
? `
|
|
55
|
+
<section class="adopted-texts-section">
|
|
56
|
+
<h2>${escapeHTML(strings.adoptedTextsHeading)}</h2>
|
|
57
|
+
${adoptedTextsHtml}
|
|
58
|
+
</section>`
|
|
59
|
+
: '';
|
|
60
|
+
const proposalsSection = proposalsHtml
|
|
61
|
+
? `
|
|
62
|
+
<section class="proposals-list">
|
|
63
|
+
<h2>${escapeHTML(strings.proposalsHeading)}</h2>
|
|
64
|
+
${proposalsHtml}
|
|
65
|
+
</section>`
|
|
66
|
+
: '';
|
|
67
|
+
return `
|
|
68
|
+
<div class="article-content">
|
|
69
|
+
<section class="lede">
|
|
70
|
+
<p>${escapeHTML(strings.lede)}</p>
|
|
71
|
+
</section>
|
|
72
|
+
${proposalsSection}
|
|
73
|
+
${adoptedTextsSection}
|
|
74
|
+
<section class="pipeline-status">
|
|
75
|
+
<h2>${escapeHTML(strings.pipelineHeading)}</h2>
|
|
76
|
+
${pipelineHtml}
|
|
77
|
+
</section>
|
|
78
|
+
${procedureSection}
|
|
79
|
+
<section class="analysis">
|
|
80
|
+
<h2>${escapeHTML(strings.analysisHeading)}</h2>
|
|
81
|
+
<p>${escapeHTML(strings.analysis)}</p>
|
|
82
|
+
</section>
|
|
83
|
+
<section class="why-this-matters">
|
|
84
|
+
<h2>${escapeHTML(editorial.whyThisMatters)}</h2>
|
|
85
|
+
<p>${escapeHTML(strings.whyThisMatters)}</p>
|
|
86
|
+
</section>
|
|
87
|
+
</div>
|
|
88
|
+
`;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=propositions-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"propositions-content.js","sourceRoot":"","sources":["../../src/generators/propositions-content.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,sCAAsC;AAEtC;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAUlF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,wBAAwB,CACtC,aAAqB,EACrB,gBAAwB,EACxB,YAAiC,EACjC,aAAqB,EACrB,OAA4B,EAC5B,IAAI,GAAG,IAAI;IAEX,MAAM,SAAS,GAAG,kBAAkB,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;IAC9D,MAAM,YAAY,GAAG,YAAY;QAC/B,CAAC,CAAC;;wCAEkC,UAAU,CAAC,OAAO,CAAC,mBAAmB,CAAC;qCAC1C,UAAU,CAAC,OAAO,CAAC,mBAAmB,CAAC;qCACvC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC,CAAC;;wCAE3D,UAAU,CAAC,OAAO,CAAC,mBAAmB,CAAC;qCAC1C,UAAU,CAAC,OAAO,CAAC,mBAAmB,CAAC;qCACvC,UAAU,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;;;MAG1E,YAAY,CAAC,YAAY,EAAE;QAC7B,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,gBAAgB,GAAG,aAAa;QACpC,CAAC,CAAC;;kBAEY,UAAU,CAAC,OAAO,CAAC,gBAAgB,CAAC;cACxC,aAAa;qBACN;QACjB,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,mBAAmB,GAAG,gBAAgB;QAC1C,CAAC,CAAC;;kBAEY,UAAU,CAAC,OAAO,CAAC,mBAAmB,CAAC;cAC3C,gBAAgB;qBACT;QACjB,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,gBAAgB,GAAG,aAAa;QACpC,CAAC,CAAC;;kBAEY,UAAU,CAAC,OAAO,CAAC,gBAAgB,CAAC;cACxC,aAAa;qBACN;QACjB,CAAC,CAAC,EAAE,CAAC;IACP,OAAO;;;iBAGQ,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC;;YAE7B,gBAAgB;YAChB,mBAAmB;;kBAEb,UAAU,CAAC,OAAO,CAAC,eAAe,CAAC;cACvC,YAAY;;YAEd,gBAAgB;;kBAEV,UAAU,CAAC,OAAO,CAAC,eAAe,CAAC;iBACpC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC;;;kBAG3B,UAAU,CAAC,SAAS,CAAC,cAAc,CAAC;iBACrC,UAAU,CAAC,OAAO,CAAC,cAAc,CAAC;;;OAG5C,CAAC;AACR,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Pre-defined semantic color categories for Sankey nodes. */
|
|
2
|
+
export type SankeyNodeColor = 'cyan' | 'magenta' | 'yellow' | 'green' | 'purple' | 'orange' | 'blue' | 'red';
|
|
3
|
+
/** A node in the Sankey diagram. */
|
|
4
|
+
export interface SankeyNode {
|
|
5
|
+
/** Unique identifier (used in flows to reference source/target). */
|
|
6
|
+
readonly id: string;
|
|
7
|
+
/** Display label. */
|
|
8
|
+
readonly label: string;
|
|
9
|
+
/** Semantic color. */
|
|
10
|
+
readonly color: SankeyNodeColor;
|
|
11
|
+
}
|
|
12
|
+
/** A directed flow between two nodes. */
|
|
13
|
+
export interface SankeyFlow {
|
|
14
|
+
/** Source node id. */
|
|
15
|
+
readonly source: string;
|
|
16
|
+
/** Target node id. */
|
|
17
|
+
readonly target: string;
|
|
18
|
+
/** Flow magnitude (relative — values are scaled to fit the SVG height). */
|
|
19
|
+
readonly value: number;
|
|
20
|
+
/** Optional label shown on the flow path. */
|
|
21
|
+
readonly label?: string | undefined;
|
|
22
|
+
}
|
|
23
|
+
/** Full Sankey diagram configuration. */
|
|
24
|
+
export interface SankeyConfig {
|
|
25
|
+
/** Array of nodes (must have at least two: one source, one target). */
|
|
26
|
+
readonly nodes: readonly SankeyNode[];
|
|
27
|
+
/** Array of directed flows between nodes. */
|
|
28
|
+
readonly flows: readonly SankeyFlow[];
|
|
29
|
+
/** Optional narrative summary. */
|
|
30
|
+
readonly summary?: string | undefined;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate a Sankey flow chart section as an HTML string with inline SVG.
|
|
34
|
+
*
|
|
35
|
+
* Returns an empty string when `config` is null/undefined or has no flows,
|
|
36
|
+
* following the same convention as `buildSwotSection` and
|
|
37
|
+
* `buildDashboardSection`.
|
|
38
|
+
*
|
|
39
|
+
* @param config - Sankey data (nodes, flows, optional summary).
|
|
40
|
+
* @param lang - BCP 47 language code for the section heading.
|
|
41
|
+
* @param heading - Optional heading override.
|
|
42
|
+
* @returns HTML string for the sankey section, or empty string.
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildSankeySection(config: SankeyConfig | null | undefined, lang?: string, heading?: string): string;
|
|
45
|
+
//# sourceMappingURL=sankey-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sankey-content.d.ts","sourceRoot":"","sources":["../../src/generators/sankey-content.ts"],"names":[],"mappings":"AAyBA,8DAA8D;AAC9D,MAAM,MAAM,eAAe,GACvB,MAAM,GACN,SAAS,GACT,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,KAAK,CAAC;AAEV,oCAAoC;AACpC,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,qBAAqB;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,sBAAsB;IACtB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;CACjC;AAED,yCAAyC;AACzC,MAAM,WAAW,UAAU;IACzB,sBAAsB;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sBAAsB;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,yCAAyC;AACzC,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,kCAAkC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAkOD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,EACvC,IAAI,GAAE,MAAa,EACnB,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CA4BR"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024-2026 Hack23 AB
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Generates a Sankey (flow) chart as inline SVG — no JavaScript or
|
|
5
|
+
* third-party libraries required.
|
|
6
|
+
*
|
|
7
|
+
* A Sankey diagram visualises quantities flowing from source nodes to target
|
|
8
|
+
* nodes. Node heights and flow-path widths are proportional to the values,
|
|
9
|
+
* making it easy to see dominant flows at a glance.
|
|
10
|
+
*
|
|
11
|
+
* Typical usage in articles:
|
|
12
|
+
* - Parliamentary flow: political group → document type → legislative outcome
|
|
13
|
+
* - Policy flow: policy domain → committee → outcome (adopted/rejected/pending)
|
|
14
|
+
* - Budget flow: programme area → committee → spending category
|
|
15
|
+
*
|
|
16
|
+
* @module generators/sankey-content
|
|
17
|
+
*/
|
|
18
|
+
import { escapeHTML } from '../utils/file-utils.js';
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Color palette
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
const NODE_COLORS = {
|
|
23
|
+
cyan: { fill: '#e3f2fd', stroke: '#1565c0', text: '#1565c0' },
|
|
24
|
+
magenta: { fill: '#fce4ec', stroke: '#c62828', text: '#c62828' },
|
|
25
|
+
yellow: { fill: '#fff8e1', stroke: '#f57f17', text: '#f57f17' },
|
|
26
|
+
green: { fill: '#e8f5e9', stroke: '#2e7d32', text: '#2e7d32' },
|
|
27
|
+
purple: { fill: '#f3e5f5', stroke: '#7b1fa2', text: '#7b1fa2' },
|
|
28
|
+
orange: { fill: '#fff3e0', stroke: '#e65100', text: '#e65100' },
|
|
29
|
+
blue: { fill: '#e8eaf6', stroke: '#283593', text: '#283593' },
|
|
30
|
+
red: { fill: '#ffebee', stroke: '#b71c1c', text: '#b71c1c' },
|
|
31
|
+
};
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Section heading labels (14 languages)
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
const SANKEY_HEADINGS = {
|
|
36
|
+
en: 'Policy Flow',
|
|
37
|
+
sv: 'Politikflöde',
|
|
38
|
+
da: 'Politikflow',
|
|
39
|
+
no: 'Politikkflyt',
|
|
40
|
+
fi: 'Politiikkavirta',
|
|
41
|
+
de: 'Politikfluss',
|
|
42
|
+
fr: 'Flux législatif',
|
|
43
|
+
es: 'Flujo legislativo',
|
|
44
|
+
nl: 'Beleidsflow',
|
|
45
|
+
ar: 'تدفق السياسات',
|
|
46
|
+
he: 'זרימת מדיניות',
|
|
47
|
+
ja: '政策フロー',
|
|
48
|
+
ko: '정책 흐름',
|
|
49
|
+
zh: '政策流向图',
|
|
50
|
+
};
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// SVG layout engine
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
const SVG_WIDTH = 600;
|
|
55
|
+
const NODE_WIDTH = 22;
|
|
56
|
+
const NODE_GAP = 14;
|
|
57
|
+
const COL_LEFT = 20;
|
|
58
|
+
const COL_RIGHT = SVG_WIDTH - NODE_WIDTH - 20;
|
|
59
|
+
/**
|
|
60
|
+
* Build a cubic bezier SVG path between two columns.
|
|
61
|
+
*
|
|
62
|
+
* @param x1 - Start x coordinate.
|
|
63
|
+
* @param y1 - Start y coordinate.
|
|
64
|
+
* @param x2 - End x coordinate.
|
|
65
|
+
* @param y2 - End y coordinate.
|
|
66
|
+
* @param h - Height of the flow path.
|
|
67
|
+
* @returns SVG path data string.
|
|
68
|
+
*/
|
|
69
|
+
function buildBezierPath(x1, y1, x2, y2, h) {
|
|
70
|
+
const mx = (x1 + x2) / 2;
|
|
71
|
+
return [
|
|
72
|
+
`M${x1},${y1}`,
|
|
73
|
+
`C${mx},${y1} ${mx},${y2} ${x2},${y2}`,
|
|
74
|
+
`L${x2},${y2 + h}`,
|
|
75
|
+
`C${mx},${y2 + h} ${mx},${y1 + h} ${x1},${y1 + h}`,
|
|
76
|
+
'Z',
|
|
77
|
+
].join(' ');
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Compute SVG element strings for the Sankey diagram.
|
|
81
|
+
*
|
|
82
|
+
* @param nodes - Array of Sankey nodes.
|
|
83
|
+
* @param flows - Array of directed flows between nodes.
|
|
84
|
+
* @param svgHeight - Height of the SVG canvas in pixels.
|
|
85
|
+
* @returns Array of SVG element strings.
|
|
86
|
+
*/
|
|
87
|
+
function layoutSankey(nodes, flows, svgHeight) {
|
|
88
|
+
const sourceIds = new Set(flows.map((f) => f.source));
|
|
89
|
+
const targetIds = new Set(flows.map((f) => f.target));
|
|
90
|
+
const valuePer = {};
|
|
91
|
+
for (const f of flows) {
|
|
92
|
+
valuePer[f.source] = (valuePer[f.source] ?? 0) + f.value;
|
|
93
|
+
valuePer[f.target] = (valuePer[f.target] ?? 0) + f.value;
|
|
94
|
+
}
|
|
95
|
+
const totalValue = Object.values(valuePer).reduce((a, b) => a + b, 0) || 1;
|
|
96
|
+
const usableHeight = svgHeight - 40;
|
|
97
|
+
const scaleFactor = usableHeight / (totalValue / 2 + (nodes.length - 1) * NODE_GAP);
|
|
98
|
+
const leftNodes = nodes.filter((n) => sourceIds.has(n.id) || !targetIds.has(n.id));
|
|
99
|
+
const rightNodes = nodes.filter((n) => targetIds.has(n.id) && !sourceIds.has(n.id));
|
|
100
|
+
function placeNodes(nds) {
|
|
101
|
+
const result = [];
|
|
102
|
+
let yOffset = 20;
|
|
103
|
+
for (const n of nds) {
|
|
104
|
+
const total = valuePer[n.id] ?? 0;
|
|
105
|
+
const height = Math.max(18, Math.round(total * scaleFactor));
|
|
106
|
+
result.push({
|
|
107
|
+
id: n.id,
|
|
108
|
+
label: n.label,
|
|
109
|
+
color: n.color,
|
|
110
|
+
totalValue: total,
|
|
111
|
+
y: yOffset,
|
|
112
|
+
height,
|
|
113
|
+
srcOffset: 0,
|
|
114
|
+
tgtOffset: 0,
|
|
115
|
+
});
|
|
116
|
+
yOffset += height + NODE_GAP;
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
const leftLayout = placeNodes(leftNodes);
|
|
121
|
+
const rightLayout = placeNodes(rightNodes);
|
|
122
|
+
const allLayout = [...leftLayout, ...rightLayout];
|
|
123
|
+
const layoutMap = new Map(allLayout.map((n) => [n.id, n]));
|
|
124
|
+
const svgElements = [];
|
|
125
|
+
for (const ln of allLayout) {
|
|
126
|
+
const palette = NODE_COLORS[ln.color] ?? NODE_COLORS.cyan;
|
|
127
|
+
const isLeft = leftLayout.some((l) => l.id === ln.id);
|
|
128
|
+
const xPos = isLeft ? COL_LEFT : COL_RIGHT;
|
|
129
|
+
const labelX = isLeft ? xPos + NODE_WIDTH + 6 : xPos - 6;
|
|
130
|
+
const textAnchor = isLeft ? 'start' : 'end';
|
|
131
|
+
svgElements.push(`<rect x="${xPos}" y="${ln.y}" width="${NODE_WIDTH}" height="${ln.height}" fill="${palette.fill}" stroke="${palette.stroke}" stroke-width="2" rx="3"/>`, `<text x="${labelX}" y="${ln.y + ln.height / 2}" text-anchor="${textAnchor}" dominant-baseline="middle" font-size="11" fill="${palette.text}" font-family="sans-serif">${escapeHTML(ln.label)}</text>`);
|
|
132
|
+
}
|
|
133
|
+
for (const flow of flows) {
|
|
134
|
+
const srcNode = layoutMap.get(flow.source);
|
|
135
|
+
const tgtNode = layoutMap.get(flow.target);
|
|
136
|
+
if (!srcNode || !tgtNode)
|
|
137
|
+
continue;
|
|
138
|
+
const srcPalette = NODE_COLORS[srcNode.color] ?? NODE_COLORS.cyan;
|
|
139
|
+
const tgtVal = valuePer[tgtNode.id] ?? 1;
|
|
140
|
+
const scaledH = Math.max(4, Math.round(((flow.value / Math.max(tgtVal, valuePer[srcNode.id] ?? 1)) *
|
|
141
|
+
(srcNode.height + tgtNode.height)) /
|
|
142
|
+
2));
|
|
143
|
+
const srcIsLeft = leftLayout.some((l) => l.id === srcNode.id);
|
|
144
|
+
const x1 = srcIsLeft ? COL_LEFT + NODE_WIDTH : COL_RIGHT;
|
|
145
|
+
const x2 = COL_RIGHT;
|
|
146
|
+
const y1 = srcNode.y + srcNode.srcOffset;
|
|
147
|
+
const y2 = tgtNode.y + tgtNode.tgtOffset;
|
|
148
|
+
srcNode.srcOffset += scaledH + 2;
|
|
149
|
+
tgtNode.tgtOffset += scaledH + 2;
|
|
150
|
+
const pathD = buildBezierPath(x1, y1, x2, y2, scaledH);
|
|
151
|
+
svgElements.push(`<path d="${pathD}" fill="${srcPalette.stroke}33" stroke="${srcPalette.stroke}" stroke-width="0.5" opacity="0.7"/>`);
|
|
152
|
+
if (flow.label) {
|
|
153
|
+
const midX = SVG_WIDTH / 2;
|
|
154
|
+
const midY = (y1 + y2) / 2 + scaledH / 2;
|
|
155
|
+
svgElements.push(`<text x="${midX}" y="${midY}" text-anchor="middle" dominant-baseline="middle" font-size="9" fill="#495057" font-family="sans-serif">${escapeHTML(flow.label)}</text>`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return svgElements;
|
|
159
|
+
}
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// Accessible table fallback
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
function renderFallbackTable(nodes, flows) {
|
|
164
|
+
const rows = flows
|
|
165
|
+
.map((f) => {
|
|
166
|
+
const srcNode = nodes.find((n) => n.id === f.source);
|
|
167
|
+
const tgtNode = nodes.find((n) => n.id === f.target);
|
|
168
|
+
return ` <tr>
|
|
169
|
+
<td>${escapeHTML(srcNode?.label ?? f.source)}</td>
|
|
170
|
+
<td>${escapeHTML(tgtNode?.label ?? f.target)}</td>
|
|
171
|
+
<td>${f.value}</td>
|
|
172
|
+
${f.label ? `<td>${escapeHTML(f.label)}</td>` : '<td></td>'}
|
|
173
|
+
</tr>`;
|
|
174
|
+
})
|
|
175
|
+
.join('\n');
|
|
176
|
+
return `<noscript>
|
|
177
|
+
<table class="sankey-fallback-table" aria-label="Sankey data">
|
|
178
|
+
<caption>Flow data</caption>
|
|
179
|
+
<thead><tr><th>Source</th><th>Target</th><th>Value</th><th>Note</th></tr></thead>
|
|
180
|
+
<tbody>
|
|
181
|
+
${rows}
|
|
182
|
+
</tbody>
|
|
183
|
+
</table>
|
|
184
|
+
</noscript>`;
|
|
185
|
+
}
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
// Public API
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
/**
|
|
190
|
+
* Generate a Sankey flow chart section as an HTML string with inline SVG.
|
|
191
|
+
*
|
|
192
|
+
* Returns an empty string when `config` is null/undefined or has no flows,
|
|
193
|
+
* following the same convention as `buildSwotSection` and
|
|
194
|
+
* `buildDashboardSection`.
|
|
195
|
+
*
|
|
196
|
+
* @param config - Sankey data (nodes, flows, optional summary).
|
|
197
|
+
* @param lang - BCP 47 language code for the section heading.
|
|
198
|
+
* @param heading - Optional heading override.
|
|
199
|
+
* @returns HTML string for the sankey section, or empty string.
|
|
200
|
+
*/
|
|
201
|
+
export function buildSankeySection(config, lang = 'en', heading) {
|
|
202
|
+
if (!config || !config.flows || config.flows.length === 0) {
|
|
203
|
+
return '';
|
|
204
|
+
}
|
|
205
|
+
const svgHeight = 340;
|
|
206
|
+
const titleText = heading?.trim() || SANKEY_HEADINGS[lang] || 'Policy Flow';
|
|
207
|
+
const summaryBlock = config.summary?.trim()
|
|
208
|
+
? ` <p class="sankey-summary">${escapeHTML(config.summary.trim())}</p>\n`
|
|
209
|
+
: '';
|
|
210
|
+
const svgElements = layoutSankey(config.nodes, config.flows, svgHeight);
|
|
211
|
+
const svgContent = svgElements.join('\n ');
|
|
212
|
+
const fallbackTable = renderFallbackTable(config.nodes, config.flows);
|
|
213
|
+
return `<section class="sankey-section" role="region" aria-label="${escapeHTML(titleText)}">
|
|
214
|
+
<h2>${escapeHTML(titleText)}</h2>
|
|
215
|
+
${summaryBlock} <div class="sankey-chart-wrapper">
|
|
216
|
+
<svg viewBox="0 0 ${SVG_WIDTH} ${svgHeight}" xmlns="http://www.w3.org/2000/svg"
|
|
217
|
+
role="img" aria-label="${escapeHTML(titleText)}"
|
|
218
|
+
style="width:100%;height:auto;max-width:${SVG_WIDTH}px;display:block;">
|
|
219
|
+
<title>${escapeHTML(titleText)}</title>
|
|
220
|
+
<rect width="${SVG_WIDTH}" height="${svgHeight}" fill="var(--surface, #f8f9fa)" rx="8"/>
|
|
221
|
+
${svgContent}
|
|
222
|
+
</svg>
|
|
223
|
+
</div>
|
|
224
|
+
${fallbackTable}
|
|
225
|
+
</section>`;
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=sankey-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sankey-content.js","sourceRoot":"","sources":["../../src/generators/sankey-content.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,sCAAsC;AAEtC;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAiDpD,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E,MAAM,WAAW,GAEb;IACF,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC7D,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAChE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC/D,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC9D,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC/D,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC/D,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC7D,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;CAC7D,CAAC;AAEF,8EAA8E;AAC9E,wCAAwC;AACxC,8EAA8E;AAE9E,MAAM,eAAe,GAAqC;IACxD,EAAE,EAAE,aAAa;IACjB,EAAE,EAAE,cAAc;IAClB,EAAE,EAAE,aAAa;IACjB,EAAE,EAAE,cAAc;IAClB,EAAE,EAAE,iBAAiB;IACrB,EAAE,EAAE,cAAc;IAClB,EAAE,EAAE,iBAAiB;IACrB,EAAE,EAAE,mBAAmB;IACvB,EAAE,EAAE,aAAa;IACjB,EAAE,EAAE,eAAe;IACnB,EAAE,EAAE,eAAe;IACnB,EAAE,EAAE,OAAO;IACX,EAAE,EAAE,OAAO;IACX,EAAE,EAAE,OAAO;CACZ,CAAC;AAEF,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E,MAAM,SAAS,GAAG,GAAG,CAAC;AACtB,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,MAAM,QAAQ,GAAG,EAAE,CAAC;AACpB,MAAM,QAAQ,GAAG,EAAE,CAAC;AACpB,MAAM,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,EAAE,CAAC;AAa9C;;;;;;;;;GASG;AACH,SAAS,eAAe,CAAC,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,CAAS;IAChF,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACzB,OAAO;QACL,IAAI,EAAE,IAAI,EAAE,EAAE;QACd,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;QACtC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE;QAClB,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE;QAClD,GAAG;KACJ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,YAAY,CACnB,KAA4B,EAC5B,KAA4B,EAC5B,SAAiB;IAEjB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAEtD,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;QACzD,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;IAC3D,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3E,MAAM,YAAY,GAAG,SAAS,GAAG,EAAE,CAAC;IACpC,MAAM,WAAW,GAAG,YAAY,GAAG,CAAC,UAAU,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;IAEpF,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACnF,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEpF,SAAS,UAAU,CAAC,GAA0B;QAC5C,MAAM,MAAM,GAAiB,EAAE,CAAC;QAChC,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC;YAC7D,MAAM,CAAC,IAAI,CAAC;gBACV,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,UAAU,EAAE,KAAK;gBACjB,CAAC,EAAE,OAAO;gBACV,MAAM;gBACN,SAAS,EAAE,CAAC;gBACZ,SAAS,EAAE,CAAC;aACb,CAAC,CAAC;YACH,OAAO,IAAI,MAAM,GAAG,QAAQ,CAAC;QAC/B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,UAAU,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,CAAC,GAAG,UAAU,EAAE,GAAG,WAAW,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAqB,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAE/E,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,WAAW,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC;QAC1D,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACtD,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;QACzD,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;QAC5C,WAAW,CAAC,IAAI,CACd,YAAY,IAAI,QAAQ,EAAE,CAAC,CAAC,YAAY,UAAU,aAAa,EAAE,CAAC,MAAM,WAAW,OAAO,CAAC,IAAI,aAAa,OAAO,CAAC,MAAM,6BAA6B,EACvJ,YAAY,MAAM,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,GAAG,CAAC,kBAAkB,UAAU,qDAAqD,OAAO,CAAC,IAAI,8BAA8B,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CACvM,CAAC;IACJ,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO;YAAE,SAAS;QAEnC,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC;QAClE,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CACtB,CAAC,EACD,IAAI,CAAC,KAAK,CACR,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;YACzD,CAAC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;YAClC,CAAC,CACJ,CACF,CAAC;QAEF,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,CAAC,CAAC;QAC9D,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;QACzD,MAAM,EAAE,GAAG,SAAS,CAAC;QACrB,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;QACzC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;QAEzC,OAAO,CAAC,SAAS,IAAI,OAAO,GAAG,CAAC,CAAC;QACjC,OAAO,CAAC,SAAS,IAAI,OAAO,GAAG,CAAC,CAAC;QAEjC,MAAM,KAAK,GAAG,eAAe,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;QACvD,WAAW,CAAC,IAAI,CACd,YAAY,KAAK,WAAW,UAAU,CAAC,MAAM,eAAe,UAAU,CAAC,MAAM,sCAAsC,CACpH,CAAC;QAEF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,GAAG,SAAS,GAAG,CAAC,CAAC;YAC3B,MAAM,IAAI,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;YACzC,WAAW,CAAC,IAAI,CACd,YAAY,IAAI,QAAQ,IAAI,2GAA2G,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CACvK,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,8EAA8E;AAC9E,4BAA4B;AAC5B,8EAA8E;AAE9E,SAAS,mBAAmB,CAAC,KAA4B,EAAE,KAA4B;IACrF,MAAM,IAAI,GAAG,KAAK;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC;QACrD,OAAO;cACC,UAAU,CAAC,OAAO,EAAE,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;cACtC,UAAU,CAAC,OAAO,EAAE,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;cACtC,CAAC,CAAC,KAAK;UACX,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW;YACvD,CAAC;IACT,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;;;;;EAKP,IAAI;;;cAGQ,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAuC,EACvC,OAAe,IAAI,EACnB,OAAgB;IAEhB,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1D,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,SAAS,GAAG,GAAG,CAAC;IACtB,MAAM,SAAS,GAAW,OAAO,EAAE,IAAI,EAAE,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC;IACpF,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE;QACzC,CAAC,CAAC,+BAA+B,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,QAAQ;QAC1E,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACxE,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,aAAa,GAAG,mBAAmB,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAEtE,OAAO,6DAA6D,UAAU,CAAC,SAAS,CAAC;QACnF,UAAU,CAAC,SAAS,CAAC;EAC3B,YAAY;wBACU,SAAS,IAAI,SAAS;kCACZ,UAAU,CAAC,SAAS,CAAC;mDACJ,SAAS;eAC7C,UAAU,CAAC,SAAS,CAAC;qBACf,SAAS,aAAa,SAAS;MAC9C,UAAU;;;IAGZ,aAAa;WACN,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Recursively collect all HTML files under a directory, returning paths
|
|
4
|
+
* relative to the project root.
|
|
5
|
+
*
|
|
6
|
+
* @param dir - Directory to scan
|
|
7
|
+
* @param rootDir - Project root for computing relative paths
|
|
8
|
+
* @returns Array of relative paths (e.g. "docs/api/index.html")
|
|
9
|
+
*/
|
|
10
|
+
export declare function collectDocsHtmlFiles(dir: string, rootDir?: string): string[];
|
|
11
|
+
/**
|
|
12
|
+
* Generate sitemap XML including index pages, news articles, sitemap HTML pages,
|
|
13
|
+
* and documentation files from the docs/ folder.
|
|
14
|
+
*
|
|
15
|
+
* @param articles - List of article filenames
|
|
16
|
+
* @param docsFiles - Relative paths to docs HTML files (e.g. "docs/api/index.html")
|
|
17
|
+
* @returns Complete sitemap XML string
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateSitemap(articles: string[], docsFiles?: string[]): string;
|
|
20
|
+
/**
|
|
21
|
+
* Get the sitemap HTML filename for a given language code.
|
|
22
|
+
*
|
|
23
|
+
* @param lang - Language code
|
|
24
|
+
* @returns Filename string
|
|
25
|
+
*/
|
|
26
|
+
export declare function getSitemapFilename(lang: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Article info extracted for sitemap HTML display
|
|
29
|
+
*/
|
|
30
|
+
interface SitemapArticleInfo {
|
|
31
|
+
filename: string;
|
|
32
|
+
date: string;
|
|
33
|
+
title: string;
|
|
34
|
+
description: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Generate a sitemap HTML page for a specific language.
|
|
38
|
+
* Lists all articles for that language with titles and descriptions,
|
|
39
|
+
* plus a high-level documentation section.
|
|
40
|
+
*
|
|
41
|
+
* @param lang - Language code
|
|
42
|
+
* @param articleInfos - Article info (title/description) for this language
|
|
43
|
+
* @param hasDocsDir - Whether the docs directory exists
|
|
44
|
+
* @returns Complete HTML document string
|
|
45
|
+
*/
|
|
46
|
+
export declare function generateSitemapHTML(lang: string, articleInfos: SitemapArticleInfo[], hasDocsDir?: boolean): string;
|
|
47
|
+
/**
|
|
48
|
+
* RSS feed item data.
|
|
49
|
+
*/
|
|
50
|
+
interface RssItem {
|
|
51
|
+
title: string;
|
|
52
|
+
link: string;
|
|
53
|
+
description: string;
|
|
54
|
+
pubDate: string;
|
|
55
|
+
lang: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Generate RSS 2.0 XML feed with all news articles across all languages.
|
|
59
|
+
* Articles are sorted newest-first. Each item includes the article language.
|
|
60
|
+
*
|
|
61
|
+
* @param articleInfos - Article metadata sorted newest first
|
|
62
|
+
* @returns Complete RSS 2.0 XML string
|
|
63
|
+
*/
|
|
64
|
+
export declare function generateRssFeed(articleInfos: RssItem[]): string;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=sitemap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../../src/generators/sitemap.ts"],"names":[],"mappings":";AAuCA;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,MAAqB,GAAG,MAAM,EAAE,CAe1F;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,SAAS,GAAE,MAAM,EAAO,GAAG,MAAM,CA6EpF;AA+HD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AA6CD;;GAEG;AACH,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,kBAAkB,EAAE,EAClC,UAAU,GAAE,OAAe,GAC1B,MAAM,CAwJR;AAED;;GAEG;AACH,UAAU,OAAO;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAiBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,MAAM,CA8B/D"}
|