riksdagsmonitor 0.8.5

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.
Files changed (40) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +999 -0
  3. package/SECURITY.md +154 -0
  4. package/dist/lib/chart-factory.d.ts +45 -0
  5. package/dist/lib/chart-factory.d.ts.map +1 -0
  6. package/dist/lib/chart-factory.js +220 -0
  7. package/dist/lib/chart-factory.js.map +1 -0
  8. package/dist/lib/data-loader.d.ts +44 -0
  9. package/dist/lib/data-loader.d.ts.map +1 -0
  10. package/dist/lib/data-loader.js +159 -0
  11. package/dist/lib/data-loader.js.map +1 -0
  12. package/dist/lib/dom-utils.d.ts +58 -0
  13. package/dist/lib/dom-utils.d.ts.map +1 -0
  14. package/dist/lib/dom-utils.js +153 -0
  15. package/dist/lib/dom-utils.js.map +1 -0
  16. package/dist/lib/error-boundary.d.ts +43 -0
  17. package/dist/lib/error-boundary.d.ts.map +1 -0
  18. package/dist/lib/error-boundary.js +101 -0
  19. package/dist/lib/error-boundary.js.map +1 -0
  20. package/dist/lib/fallback-ui.d.ts +28 -0
  21. package/dist/lib/fallback-ui.d.ts.map +1 -0
  22. package/dist/lib/fallback-ui.js +65 -0
  23. package/dist/lib/fallback-ui.js.map +1 -0
  24. package/dist/lib/index.d.ts +20 -0
  25. package/dist/lib/index.d.ts.map +1 -0
  26. package/dist/lib/index.js +20 -0
  27. package/dist/lib/index.js.map +1 -0
  28. package/dist/lib/logger.d.ts +19 -0
  29. package/dist/lib/logger.d.ts.map +1 -0
  30. package/dist/lib/logger.js +31 -0
  31. package/dist/lib/logger.js.map +1 -0
  32. package/dist/lib/theme.d.ts +107 -0
  33. package/dist/lib/theme.d.ts.map +1 -0
  34. package/dist/lib/theme.js +207 -0
  35. package/dist/lib/theme.js.map +1 -0
  36. package/dist/lib/types.d.ts +95 -0
  37. package/dist/lib/types.d.ts.map +1 -0
  38. package/dist/lib/types.js +14 -0
  39. package/dist/lib/types.js.map +1 -0
  40. package/package.json +140 -0
package/SECURITY.md ADDED
@@ -0,0 +1,154 @@
1
+ <p align="center">
2
+ <img src="https://hack23.com/icon-192.png" alt="Hack23 Logo" width="192" height="192">
3
+ </p>
4
+
5
+ <h1 align="center">🔐 Security Policy — Riksdagsmonitor</h1>
6
+
7
+ <p align="center">
8
+ <strong>🛡️ Security Through Transparency and Vulnerability Management</strong><br>
9
+ <em>🎯 Defense-in-Depth Architecture for Democratic Intelligence</em>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="#"><img src="https://img.shields.io/badge/Owner-CEO-0A66C2?style=for-the-badge" alt="Owner"/></a>
14
+ <a href="#"><img src="https://img.shields.io/badge/Version-1.0-555?style=for-the-badge" alt="Version"/></a>
15
+ <a href="#"><img src="https://img.shields.io/badge/Effective-2026--02--20-success?style=for-the-badge" alt="Effective Date"/></a>
16
+ <a href="#"><img src="https://img.shields.io/badge/Review-Quarterly-orange?style=for-the-badge" alt="Review Cycle"/></a>
17
+ </p>
18
+
19
+ **📋 Document Owner:** CEO | **📄 Version:** 1.0 | **📅 Last Updated:** 2026-02-20 (UTC)
20
+ **🔄 Review Cycle:** Quarterly | **⏰ Next Review:** 2026-05-20
21
+ **🏢 Owner:** Hack23 AB (Org.nr 5595347807) | **🏷️ Classification:** Public
22
+
23
+ ---
24
+
25
+ ## 🎯 **Purpose Statement**
26
+
27
+ This security policy establishes vulnerability disclosure and incident response procedures for Riksdagsmonitor, implementing [Vulnerability Management](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Vulnerability_Management.md) and [Incident Response Plan](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Incident_Response_Plan.md) from Hack23 AB's ISMS framework.
28
+
29
+ Our security approach demonstrates our commitment to **transparency** and **operational excellence**, ensuring that vulnerabilities are managed systematically with documented response times and coordinated disclosure processes.
30
+
31
+ *— James Pether Sörling, CEO/Founder*
32
+
33
+ ---
34
+
35
+ ## Supported Versions
36
+
37
+ This project is under active development, and we provide security updates for the latest version only.
38
+
39
+ | Version | Supported | ISMS Policy |
40
+ | ------- | ------------------ | ----------- |
41
+ | latest | :white_check_mark: | [Vulnerability Management](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Vulnerability_Management.md) |
42
+
43
+ ## Security Posture
44
+
45
+ Riksdagsmonitor maintains strong security practices as documented in our [Security Architecture](SECURITY_ARCHITECTURE.md):
46
+
47
+ ### Current Security Controls
48
+
49
+ - ✅ **Static Site Architecture** — No server-side code execution, no database vulnerabilities
50
+ - ✅ **HTTPS-Only** — TLS 1.3 via AWS CloudFront and GitHub Pages
51
+ - ✅ **Automated Security Scanning** — CodeQL, Dependabot, Secret Scanning
52
+ - ✅ **Supply Chain Security** — SHA-pinned GitHub Actions, step-security/harden-runner
53
+ - ✅ **Multi-Region Availability** — AWS CloudFront (us-east-1 primary, eu-west-1 replica) with GitHub Pages DR
54
+ - ✅ **SLSA Build Provenance** — Attestation for build integrity
55
+ - ✅ **Content Integrity** — Subresource Integrity (SRI) for CDN assets
56
+ - ✅ **Security Headers** — CSP, HSTS, X-Frame-Options, X-Content-Type-Options
57
+
58
+ **Evidence:**
59
+ - [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/Hack23/riksdagsmonitor/badge)](https://scorecard.dev/viewer/?uri=github.com/Hack23/riksdagsmonitor)
60
+ - [Security Overview](https://github.com/Hack23/riksdagsmonitor/security)
61
+
62
+ ---
63
+
64
+ ## Reporting a Vulnerability
65
+
66
+ We take the security of Riksdagsmonitor seriously. If you have found a potential security vulnerability, we kindly ask you to report it privately, so that we can assess and address the issue before it becomes publicly known.
67
+
68
+ ### What Constitutes a Vulnerability
69
+
70
+ A vulnerability is a weakness or flaw in the project that can be exploited to compromise the security, integrity, or availability of the system or its data. Examples include, but are not limited to:
71
+
72
+ - Cross-site scripting (XSS) in generated content
73
+ - Insecure external resource loading
74
+ - Exposed secrets or credentials
75
+ - Supply chain vulnerabilities in dependencies
76
+ - Content injection through data pipelines
77
+
78
+ ### How to Privately Report a Vulnerability using GitHub
79
+
80
+ 1. On GitHub.com, navigate to the main page of the [riksdagsmonitor repository](https://github.com/Hack23/riksdagsmonitor).
81
+ 2. Under the repository name, click **Security**.
82
+ 3. In the left sidebar, under "Reporting", click **Advisories**.
83
+ 4. Click **Report a vulnerability** to open the advisory form.
84
+ 5. Fill in the advisory details form with as much information as possible.
85
+ 6. At the bottom of the form, click **Submit report**.
86
+
87
+ ### Disclosure Timeline
88
+
89
+ Upon receipt of a vulnerability report, our team will:
90
+
91
+ 1. Acknowledge the report within **48 hours**
92
+ 2. Validate the vulnerability within **7 days**
93
+ 3. Develop and release a patch or mitigation within **30 days** (depending on complexity and severity)
94
+ 4. Publish a security advisory with a detailed description of the vulnerability and the fix
95
+
96
+ ### Recognition and Anonymity
97
+
98
+ We appreciate your effort in helping us maintain a secure project. If your report results in a confirmed security fix, we will recognize your contribution in the release notes, unless you request to remain anonymous.
99
+
100
+ ---
101
+
102
+ ## 🔐 ISMS Framework Integration
103
+
104
+ Riksdagsmonitor's security practices are part of Hack23 AB's comprehensive Information Security Management System (ISMS):
105
+
106
+ ### 📋 Related ISMS Policies
107
+
108
+ | 🛡️ **Policy** | 📊 **Application to Riksdagsmonitor** |
109
+ |--------------|--------------------------------------|
110
+ | [Vulnerability Management](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Vulnerability_Management.md) | 48h response SLA, coordinated disclosure process |
111
+ | [Incident Response Plan](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Incident_Response_Plan.md) | P1-P4 incident classification, escalation procedures |
112
+ | [Secure Development Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Secure_Development_Policy.md) | Security testing requirements, code review standards |
113
+ | [Information Security Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Information_Security_Policy.md) | Overall security governance framework |
114
+ | [Network Security Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Network_Security_Policy.md) | HTTPS-only, TLS 1.3, CDN security |
115
+ | [Cryptography Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Cryptography_Policy.md) | TLS configuration, SRI hashes |
116
+
117
+ ### 🔍 Comprehensive Security Documentation
118
+
119
+ - **🛡️ Security Architecture:** [SECURITY_ARCHITECTURE.md](SECURITY_ARCHITECTURE.md) — Defense-in-depth controls
120
+ - **🎯 Threat Model:** [THREAT_MODEL.md](THREAT_MODEL.md) — STRIDE analysis, MITRE ATT&CK mapping
121
+ - **🔮 Future Security:** [FUTURE_SECURITY_ARCHITECTURE.md](FUTURE_SECURITY_ARCHITECTURE.md) — Security roadmap
122
+ - **🔧 CI/CD Security:** [WORKFLOWS.md](WORKFLOWS.md) — Pipeline security controls
123
+
124
+ ---
125
+
126
+ ## 📚 Related Documents
127
+
128
+ ### 🔐 Security & Architecture
129
+ - [🛡️ Security Architecture](SECURITY_ARCHITECTURE.md) — System security design
130
+ - [🎯 Threat Model](THREAT_MODEL.md) — Comprehensive threat analysis
131
+ - [🔮 Future Security Architecture](FUTURE_SECURITY_ARCHITECTURE.md) — Security roadmap
132
+ - [🏗️ Architecture](ARCHITECTURE.md) — System architecture (C4 models)
133
+ - [🔧 Workflows](WORKFLOWS.md) — CI/CD pipeline documentation
134
+
135
+ ### 📋 Project Governance
136
+ - [🤝 Contributing Guidelines](CONTRIBUTING.md) — Secure contribution process
137
+ - [📜 Code of Conduct](CODE_OF_CONDUCT.md) — Community standards
138
+ - [📋 README](README.md) — Project overview and classification
139
+
140
+ ### 🛡️ ISMS Policies (Hack23 AB)
141
+ - [🔐 Information Security Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Information_Security_Policy.md)
142
+ - [🔍 Vulnerability Management](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Vulnerability_Management.md)
143
+ - [🚨 Incident Response Plan](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Incident_Response_Plan.md)
144
+ - [🛠️ Secure Development Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Secure_Development_Policy.md)
145
+
146
+ ---
147
+
148
+ **📋 Document Control:**
149
+ **✅ Approved by:** James Pether Sörling, CEO
150
+ **📤 Distribution:** Public
151
+ **🏷️ Classification:** [![Confidentiality: Public](https://img.shields.io/badge/C-Public-lightgrey?style=flat-square)](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md#confidentiality-levels)
152
+ **📅 Effective Date:** 2026-02-20
153
+ **⏰ Next Review:** 2026-05-20
154
+ **🎯 Framework Compliance:** [![ISO 27001](https://img.shields.io/badge/ISO_27001-2022_Aligned-blue?style=flat-square&logo=iso&logoColor=white)](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md) [![NIST CSF 2.0](https://img.shields.io/badge/NIST_CSF-2.0_Aligned-green?style=flat-square&logo=nist&logoColor=white)](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md) [![CIS Controls](https://img.shields.io/badge/CIS_Controls-v8.1_Aligned-orange?style=flat-square&logo=cisecurity&logoColor=white)](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md)
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @module Shared/ChartFactory
3
+ * @description Centralized Chart.js creation and configuration.
4
+ * Replaces 51+ independent `new Chart()` calls with consistent theming,
5
+ * responsive behavior, and accessibility features.
6
+ *
7
+ * @security No inline scripts — all Chart.js configuration is programmatic
8
+
9
+ *
10
+ * @intelligence Standardized intelligence visualization factory — ensures all analytical charts (risk heat maps, coalition networks, electoral forecasts) maintain consistent OSINT presentation standards with accessibility compliance for briefing-quality output.
11
+ *
12
+ * @business Development velocity multiplier — centralizing Chart.js configuration eliminates per-dashboard setup cost (reduced from 51+ `new Chart()` calls). Enables rapid prototyping of new intelligence products with predictable quality and performance.
13
+ *
14
+ * @marketing Visual quality assurance — every chart produced is screenshot-ready for social media, press releases, and reports. Consistent styling builds brand recognition. Responsive behavior ensures mobile-quality content for all distribution channels.
15
+ * */
16
+ import type { Chart as ChartType, ChartConfiguration, ChartTypeRegistry } from 'chart.js';
17
+ import { THEME_COLORS, CHART_PALETTE, BREAKPOINTS, getActiveThemeColors, getChartPalette } from './theme.js';
18
+ export { THEME_COLORS, CHART_PALETTE, BREAKPOINTS, getActiveThemeColors, getChartPalette };
19
+ /**
20
+ * Get responsive chart options based on current viewport.
21
+ */
22
+ export declare function getResponsiveOptions(): Record<string, unknown>;
23
+ /**
24
+ * Create a Chart.js chart with consistent theming and responsive behavior.
25
+ *
26
+ * @param canvas - Canvas element or its ID
27
+ * @param config - Chart.js configuration
28
+ * @param containerId - Optional container ID for loading/error states
29
+ * @returns The Chart instance
30
+ */
31
+ export declare function createChart<T extends keyof ChartTypeRegistry>(canvas: HTMLCanvasElement | string, config: ChartConfiguration<T>, containerId?: string): ChartType<T>;
32
+ /**
33
+ * Safely initialize a dashboard section.
34
+ * Shows loading state, runs the initializer, shows error state on failure.
35
+ */
36
+ export declare function initDashboardSection(containerId: string, initializer: () => Promise<void>, loadingMessage?: string): Promise<void>;
37
+ /**
38
+ * Add keyboard navigation to a Chart.js chart for accessibility.
39
+ */
40
+ export declare function addChartKeyboardNav(chart: ChartType, canvas: HTMLCanvasElement): void;
41
+ /**
42
+ * Create a resize handler that re-creates charts on breakpoint changes.
43
+ */
44
+ export declare function createResizeHandler(callback: () => void): () => void;
45
+ //# sourceMappingURL=chart-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-factory.d.ts","sourceRoot":"","sources":["../../src/browser/shared/chart-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;KAcK;AAEL,OAAO,KAAK,EAAE,KAAK,IAAI,SAAS,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAK7G,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,CAAC;AAY3F;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkD9D;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAC3D,MAAM,EAAE,iBAAiB,GAAG,MAAM,EAClC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC7B,WAAW,CAAC,EAAE,MAAM,GACnB,SAAS,CAAC,CAAC,CAAC,CAkCd;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAChC,cAAc,SAAoB,GACjC,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,iBAAiB,GACxB,IAAI,CAwBN;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAiBpE"}
@@ -0,0 +1,220 @@
1
+ /**
2
+ * @module Shared/ChartFactory
3
+ * @description Centralized Chart.js creation and configuration.
4
+ * Replaces 51+ independent `new Chart()` calls with consistent theming,
5
+ * responsive behavior, and accessibility features.
6
+ *
7
+ * @security No inline scripts — all Chart.js configuration is programmatic
8
+
9
+ *
10
+ * @intelligence Standardized intelligence visualization factory — ensures all analytical charts (risk heat maps, coalition networks, electoral forecasts) maintain consistent OSINT presentation standards with accessibility compliance for briefing-quality output.
11
+ *
12
+ * @business Development velocity multiplier — centralizing Chart.js configuration eliminates per-dashboard setup cost (reduced from 51+ `new Chart()` calls). Enables rapid prototyping of new intelligence products with predictable quality and performance.
13
+ *
14
+ * @marketing Visual quality assurance — every chart produced is screenshot-ready for social media, press releases, and reports. Consistent styling builds brand recognition. Responsive behavior ensures mobile-quality content for all distribution channels.
15
+ * */
16
+ import { THEME_COLORS, CHART_PALETTE, BREAKPOINTS, getActiveThemeColors, getChartPalette } from './theme.js';
17
+ import { showLoadingState, showErrorState, hideStateOverlays } from './dom-utils.js';
18
+ import { logger } from './logger.js';
19
+ // Re-export for convenience
20
+ export { THEME_COLORS, CHART_PALETTE, BREAKPOINTS, getActiveThemeColors, getChartPalette };
21
+ /**
22
+ * Get the Chart constructor.
23
+ * Works with both global `Chart` (from script tag) and ES module import.
24
+ */
25
+ function getChart() {
26
+ const g = globalThis;
27
+ if (g.Chart)
28
+ return g.Chart;
29
+ throw new Error('Chart.js not loaded');
30
+ }
31
+ /**
32
+ * Get responsive chart options based on current viewport.
33
+ */
34
+ export function getResponsiveOptions() {
35
+ const width = window.innerWidth;
36
+ const isMobile = width < BREAKPOINTS.tablet;
37
+ const isTablet = width >= BREAKPOINTS.tablet && width < BREAKPOINTS.desktop;
38
+ const theme = getActiveThemeColors();
39
+ return {
40
+ responsive: true,
41
+ maintainAspectRatio: false,
42
+ animation: { duration: isMobile ? 0 : 400 },
43
+ plugins: {
44
+ legend: {
45
+ position: isMobile ? 'bottom' : 'top',
46
+ labels: {
47
+ boxWidth: isMobile ? 8 : 12,
48
+ padding: isMobile ? 4 : 8,
49
+ font: { size: isMobile ? 10 : (isTablet ? 11 : 12) },
50
+ color: theme.bodyText,
51
+ },
52
+ },
53
+ tooltip: {
54
+ backgroundColor: theme.tooltipBg,
55
+ titleColor: theme.cyan,
56
+ bodyColor: theme.bodyText,
57
+ borderColor: theme.cyan,
58
+ borderWidth: 1,
59
+ padding: isMobile ? 6 : 10,
60
+ titleFont: { size: isMobile ? 11 : 13, weight: 'bold' },
61
+ bodyFont: { size: isMobile ? 10 : 12 },
62
+ cornerRadius: 4,
63
+ },
64
+ },
65
+ scales: {
66
+ x: {
67
+ ticks: {
68
+ color: theme.tickColor,
69
+ font: { size: isMobile ? 9 : 11 },
70
+ maxRotation: isMobile ? 45 : 0,
71
+ },
72
+ grid: { color: theme.gridColor },
73
+ },
74
+ y: {
75
+ ticks: {
76
+ color: theme.tickColor,
77
+ font: { size: isMobile ? 9 : 11 },
78
+ },
79
+ grid: { color: theme.gridColor },
80
+ },
81
+ },
82
+ };
83
+ }
84
+ /**
85
+ * Create a Chart.js chart with consistent theming and responsive behavior.
86
+ *
87
+ * @param canvas - Canvas element or its ID
88
+ * @param config - Chart.js configuration
89
+ * @param containerId - Optional container ID for loading/error states
90
+ * @returns The Chart instance
91
+ */
92
+ export function createChart(canvas, config, containerId) {
93
+ const ChartCtor = getChart();
94
+ const canvasEl = typeof canvas === 'string'
95
+ ? document.getElementById(canvas)
96
+ : canvas;
97
+ if (!canvasEl) {
98
+ throw new Error(`Canvas element not found: ${canvas}`);
99
+ }
100
+ // Merge responsive options with user config
101
+ const responsive = getResponsiveOptions();
102
+ const mergedConfig = {
103
+ ...config,
104
+ options: deepMerge(responsive, config.options ?? {}),
105
+ };
106
+ const container = containerId
107
+ ? document.getElementById(containerId)
108
+ : canvasEl.parentElement;
109
+ if (container) {
110
+ hideStateOverlays(container);
111
+ }
112
+ try {
113
+ return new ChartCtor(canvasEl, mergedConfig);
114
+ }
115
+ catch (error) {
116
+ logger.error(`Failed to create chart:`, error);
117
+ if (container) {
118
+ showErrorState(container, 'Failed to render chart');
119
+ }
120
+ throw error;
121
+ }
122
+ }
123
+ /**
124
+ * Safely initialize a dashboard section.
125
+ * Shows loading state, runs the initializer, shows error state on failure.
126
+ */
127
+ export async function initDashboardSection(containerId, initializer, loadingMessage = 'Loading data...') {
128
+ const container = document.getElementById(containerId);
129
+ if (!container) {
130
+ logger.debug(`Container #${containerId} not found — skipping`);
131
+ return;
132
+ }
133
+ showLoadingState(container, loadingMessage);
134
+ try {
135
+ await initializer();
136
+ hideStateOverlays(container);
137
+ }
138
+ catch (error) {
139
+ logger.error(`Dashboard section ${containerId} failed:`, error);
140
+ showErrorState(container, `Failed to load ${containerId.replace(/-/g, ' ')}`);
141
+ }
142
+ }
143
+ /**
144
+ * Add keyboard navigation to a Chart.js chart for accessibility.
145
+ */
146
+ export function addChartKeyboardNav(chart, canvas) {
147
+ canvas.setAttribute('tabindex', '0');
148
+ canvas.setAttribute('role', 'img');
149
+ canvas.addEventListener('keydown', (e) => {
150
+ const meta = chart.getActiveElements();
151
+ const currentIndex = meta.length > 0 ? meta[0].index : -1;
152
+ const datasetIndex = meta.length > 0 ? meta[0].datasetIndex : 0;
153
+ const maxIndex = (chart.data.datasets[datasetIndex]?.data?.length ?? 1) - 1;
154
+ let newIndex = currentIndex;
155
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
156
+ newIndex = Math.min(currentIndex + 1, maxIndex);
157
+ e.preventDefault();
158
+ }
159
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
160
+ newIndex = Math.max(currentIndex - 1, 0);
161
+ e.preventDefault();
162
+ }
163
+ if (newIndex !== currentIndex && newIndex >= 0) {
164
+ chart.setActiveElements([{ datasetIndex, index: newIndex }]);
165
+ chart.update();
166
+ }
167
+ });
168
+ }
169
+ /**
170
+ * Create a resize handler that re-creates charts on breakpoint changes.
171
+ */
172
+ export function createResizeHandler(callback) {
173
+ let currentBreakpoint = getBreakpoint();
174
+ let resizeTimer;
175
+ const handler = () => {
176
+ clearTimeout(resizeTimer);
177
+ resizeTimer = setTimeout(() => {
178
+ const newBreakpoint = getBreakpoint();
179
+ if (newBreakpoint !== currentBreakpoint) {
180
+ currentBreakpoint = newBreakpoint;
181
+ callback();
182
+ }
183
+ }, 250);
184
+ };
185
+ window.addEventListener('resize', handler);
186
+ return () => window.removeEventListener('resize', handler);
187
+ }
188
+ function getBreakpoint() {
189
+ const w = window.innerWidth;
190
+ if (w < BREAKPOINTS.tablet)
191
+ return 'mobile';
192
+ if (w < BREAKPOINTS.desktop)
193
+ return 'tablet';
194
+ if (w < BREAKPOINTS.large)
195
+ return 'desktop';
196
+ return 'large';
197
+ }
198
+ /**
199
+ * Deep merge two objects (simple version for chart configs).
200
+ */
201
+ function deepMerge(target, source) {
202
+ const result = { ...target };
203
+ for (const key of Object.keys(source)) {
204
+ const sourceVal = source[key];
205
+ const targetVal = result[key];
206
+ if (sourceVal &&
207
+ typeof sourceVal === 'object' &&
208
+ !Array.isArray(sourceVal) &&
209
+ targetVal &&
210
+ typeof targetVal === 'object' &&
211
+ !Array.isArray(targetVal)) {
212
+ result[key] = deepMerge(targetVal, sourceVal);
213
+ }
214
+ else {
215
+ result[key] = sourceVal;
216
+ }
217
+ }
218
+ return result;
219
+ }
220
+ //# sourceMappingURL=chart-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-factory.js","sourceRoot":"","sources":["../../src/browser/shared/chart-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;KAcK;AAGL,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7G,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,4BAA4B;AAC5B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,CAAC;AAE3F;;;GAGG;AACH,SAAS,QAAQ;IACf,MAAM,CAAC,GAAG,UAAqC,CAAC;IAChD,IAAI,CAAC,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC,KAAyB,CAAC;IAChD,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC;IAChC,MAAM,QAAQ,GAAG,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC;IAC5C,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW,CAAC,MAAM,IAAI,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC;IAC5E,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IAErC,OAAO;QACL,UAAU,EAAE,IAAI;QAChB,mBAAmB,EAAE,KAAK;QAC1B,SAAS,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE;QAC3C,OAAO,EAAE;YACP,MAAM,EAAE;gBACN,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAiB,CAAC,CAAC,CAAC,KAAc;gBACvD,MAAM,EAAE;oBACN,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;oBAC3B,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBACzB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;oBACpD,KAAK,EAAE,KAAK,CAAC,QAAQ;iBACtB;aACF;YACD,OAAO,EAAE;gBACP,eAAe,EAAE,KAAK,CAAC,SAAS;gBAChC,UAAU,EAAE,KAAK,CAAC,IAAI;gBACtB,SAAS,EAAE,KAAK,CAAC,QAAQ;gBACzB,WAAW,EAAE,KAAK,CAAC,IAAI;gBACvB,WAAW,EAAE,CAAC;gBACd,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;gBAC1B,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,MAAe,EAAE;gBAChE,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;gBACtC,YAAY,EAAE,CAAC;aAChB;SACF;QACD,MAAM,EAAE;YACN,CAAC,EAAE;gBACD,KAAK,EAAE;oBACL,KAAK,EAAE,KAAK,CAAC,SAAS;oBACtB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;oBACjC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;iBAC/B;gBACD,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,SAAS,EAAE;aACjC;YACD,CAAC,EAAE;gBACD,KAAK,EAAE;oBACL,KAAK,EAAE,KAAK,CAAC,SAAS;oBACtB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;iBAClC;gBACD,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,SAAS,EAAE;aACjC;SACF;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,MAAkC,EAClC,MAA6B,EAC7B,WAAoB;IAEpB,MAAM,SAAS,GAAG,QAAQ,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,OAAO,MAAM,KAAK,QAAQ;QACzC,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAA6B;QAC7D,CAAC,CAAC,MAAM,CAAC;IAEX,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,6BAA6B,MAAM,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,4CAA4C;IAC5C,MAAM,UAAU,GAAG,oBAAoB,EAAE,CAAC;IAC1C,MAAM,YAAY,GAA0B;QAC1C,GAAG,MAAM;QACT,OAAO,EAAE,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,CAAqC;KACzF,CAAC;IAEF,MAAM,SAAS,GAAG,WAAW;QAC3B,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,WAAW,CAAC;QACtC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC;IAE3B,IAAI,SAAS,EAAE,CAAC;QACd,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,CAAC;QACH,OAAO,IAAI,SAAS,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;QAC/C,IAAI,SAAS,EAAE,CAAC;YACd,cAAc,CAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC;QACtD,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,WAAmB,EACnB,WAAgC,EAChC,cAAc,GAAG,iBAAiB;IAElC,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IACvD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,cAAc,WAAW,uBAAuB,CAAC,CAAC;QAC/D,OAAO;IACT,CAAC;IAED,gBAAgB,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAE5C,IAAI,CAAC;QACH,MAAM,WAAW,EAAE,CAAC;QACpB,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,qBAAqB,WAAW,UAAU,EAAE,KAAK,CAAC,CAAC;QAChE,cAAc,CACZ,SAAS,EACT,kBAAkB,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CACnD,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAgB,EAChB,MAAyB;IAEzB,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACrC,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAEnC,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,CAAgB,EAAE,EAAE;QACtD,MAAM,IAAI,GAAG,KAAK,CAAC,iBAAiB,EAAE,CAAC;QACvC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAE5E,IAAI,QAAQ,GAAG,YAAY,CAAC;QAC5B,IAAI,CAAC,CAAC,GAAG,KAAK,YAAY,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW,EAAE,CAAC;YACpD,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;YAChD,CAAC,CAAC,cAAc,EAAE,CAAC;QACrB,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YACxD,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;YACzC,CAAC,CAAC,cAAc,EAAE,CAAC;QACrB,CAAC;QAED,IAAI,QAAQ,KAAK,YAAY,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YAC/C,KAAK,CAAC,iBAAiB,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;YAC7D,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAoB;IACtD,IAAI,iBAAiB,GAAG,aAAa,EAAE,CAAC;IACxC,IAAI,WAA0C,CAAC;IAE/C,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,YAAY,CAAC,WAAW,CAAC,CAAC;QAC1B,WAAW,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,MAAM,aAAa,GAAG,aAAa,EAAE,CAAC;YACtC,IAAI,aAAa,KAAK,iBAAiB,EAAE,CAAC;gBACxC,iBAAiB,GAAG,aAAa,CAAC;gBAClC,QAAQ,EAAE,CAAC;YACb,CAAC;QACH,CAAC,EAAE,GAAG,CAAC,CAAC;IACV,CAAC,CAAC;IAEF,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC3C,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,aAAa;IACpB,MAAM,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC;IAC5B,IAAI,CAAC,GAAG,WAAW,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC5C,IAAI,CAAC,GAAG,WAAW,CAAC,OAAO;QAAE,OAAO,QAAQ,CAAC;IAC7C,IAAI,CAAC,GAAG,WAAW,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC5C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAChB,MAA+B,EAC/B,MAA+B;IAE/B,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9B,IACE,SAAS;YACT,OAAO,SAAS,KAAK,QAAQ;YAC7B,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;YACzB,SAAS;YACT,OAAO,SAAS,KAAK,QAAQ;YAC7B,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EACzB,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CACrB,SAAoC,EACpC,SAAoC,CACrC,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @module Shared/DataLoader
3
+ * @description Unified data fetching with fallback, caching, and retry logic.
4
+ * Replaces 6+ independent data loading implementations across dashboards.
5
+ *
6
+ * Features:
7
+ * - Local-first with remote fallback
8
+ * - localStorage caching with TTL
9
+ * - Retry with exponential backoff
10
+ * - CSV parsing via PapaParse (CSP-compatible) with simple fallback
11
+ * - JSON and text response handling
12
+
13
+ *
14
+ * @intelligence Resilient OSINT data acquisition pipeline — multi-source intelligence data loading with local-first strategy, remote fallback, localStorage caching (TTL-based), retry with exponential backoff, and CSV/JSON parsing. Ensures continuous intelligence availability even during source outages.
15
+ *
16
+ * @business Platform reliability foundation — data loading resilience directly impacts user experience KPIs (page load time, error rate, Time to Interactive). Caching reduces infrastructure costs and enables offline-capable future PWA offering.
17
+ *
18
+ * @marketing Performance marketing enabler — fast, reliable data loading supports Core Web Vitals targets (LCP < 2.5s, FID < 100ms) critical for SEO ranking and user retention. Reliability metrics are a key selling point for B2G/enterprise prospects.
19
+ * */
20
+ import type { CSVRow, DataSource, LoadOptions } from './types.js';
21
+ /**
22
+ * Load text data from a data source with fallback and caching.
23
+ */
24
+ export declare function loadText(source: DataSource, options?: LoadOptions): Promise<string>;
25
+ /**
26
+ * Load and parse CSV data from a data source.
27
+ * Uses PapaParse (CSP-compatible) with a simple CSV fallback parser.
28
+ */
29
+ export declare function loadCSV(source: DataSource, options?: LoadOptions): Promise<CSVRow[]>;
30
+ /**
31
+ * Load and parse JSON data from a data source.
32
+ */
33
+ export declare function loadJSON<T = unknown>(source: DataSource, options?: LoadOptions): Promise<T>;
34
+ /**
35
+ * Parse CSV text into rows.
36
+ * Uses PapaParse if available (CSP-compatible), falls back to a simple CSV parser.
37
+ * Does NOT use d3.csvParse as it requires unsafe-eval in CSP.
38
+ */
39
+ export declare function parseCSV(text: string): CSVRow[];
40
+ /**
41
+ * Create a DataSource from local path with optional GitHub raw fallback.
42
+ */
43
+ export declare function createDataSource(localPath: string, repoPath?: string): DataSource;
44
+ //# sourceMappingURL=data-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-loader.d.ts","sourceRoot":"","sources":["../../src/browser/shared/data-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;KAkBK;AAGL,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAgElE;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,MAAM,CAAC,CAmCjB;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAC3B,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,MAAM,EAAE,CAAC,CAGnB;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,CAAC,GAAG,OAAO,EACxC,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,CAAC,CAAC,CAGZ;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAqB/C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,GAChB,UAAU,CAQZ"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * @module Shared/DataLoader
3
+ * @description Unified data fetching with fallback, caching, and retry logic.
4
+ * Replaces 6+ independent data loading implementations across dashboards.
5
+ *
6
+ * Features:
7
+ * - Local-first with remote fallback
8
+ * - localStorage caching with TTL
9
+ * - Retry with exponential backoff
10
+ * - CSV parsing via PapaParse (CSP-compatible) with simple fallback
11
+ * - JSON and text response handling
12
+
13
+ *
14
+ * @intelligence Resilient OSINT data acquisition pipeline — multi-source intelligence data loading with local-first strategy, remote fallback, localStorage caching (TTL-based), retry with exponential backoff, and CSV/JSON parsing. Ensures continuous intelligence availability even during source outages.
15
+ *
16
+ * @business Platform reliability foundation — data loading resilience directly impacts user experience KPIs (page load time, error rate, Time to Interactive). Caching reduces infrastructure costs and enables offline-capable future PWA offering.
17
+ *
18
+ * @marketing Performance marketing enabler — fast, reliable data loading supports Core Web Vitals targets (LCP < 2.5s, FID < 100ms) critical for SEO ranking and user retention. Reliability metrics are a key selling point for B2G/enterprise prospects.
19
+ * */
20
+ import { logger } from './logger.js';
21
+ const DEFAULT_CACHE_TTL = 7 * 24 * 60 * 60 * 1000; // 7 days
22
+ const DEFAULT_RETRIES = 3;
23
+ const DEFAULT_RETRY_BACKOFF = 2000;
24
+ /**
25
+ * Fetch data from a URL with retry logic.
26
+ */
27
+ async function fetchWithRetry(url, retries, backoff) {
28
+ for (let attempt = 1; attempt <= retries; attempt++) {
29
+ try {
30
+ const response = await fetch(url);
31
+ if (response.ok)
32
+ return response;
33
+ logger.warn(`Fetch attempt ${attempt}/${retries} failed for ${url}: ${response.status}`);
34
+ }
35
+ catch (error) {
36
+ logger.warn(`Fetch attempt ${attempt}/${retries} error for ${url}:`, error);
37
+ }
38
+ if (attempt < retries) {
39
+ await new Promise((resolve) => setTimeout(resolve, backoff * attempt));
40
+ }
41
+ }
42
+ throw new Error(`Failed to fetch ${url} after ${retries} attempts`);
43
+ }
44
+ /**
45
+ * Get data from localStorage cache if valid.
46
+ */
47
+ function getFromCache(key, ttl) {
48
+ try {
49
+ const raw = localStorage.getItem(key);
50
+ if (!raw)
51
+ return null;
52
+ const entry = JSON.parse(raw);
53
+ if (Date.now() - entry.timestamp > ttl) {
54
+ localStorage.removeItem(key);
55
+ return null;
56
+ }
57
+ return entry.data;
58
+ }
59
+ catch {
60
+ return null;
61
+ }
62
+ }
63
+ /**
64
+ * Store data in localStorage cache.
65
+ */
66
+ function setCache(key, data) {
67
+ try {
68
+ const entry = { data, timestamp: Date.now() };
69
+ localStorage.setItem(key, JSON.stringify(entry));
70
+ }
71
+ catch {
72
+ logger.warn('Failed to cache data — localStorage may be full');
73
+ }
74
+ }
75
+ /**
76
+ * Load text data from a data source with fallback and caching.
77
+ */
78
+ export async function loadText(source, options = {}) {
79
+ const { cacheKey, cacheTTL = DEFAULT_CACHE_TTL, retries = DEFAULT_RETRIES, retryBackoff = DEFAULT_RETRY_BACKOFF, } = options;
80
+ // Check cache first
81
+ if (cacheKey) {
82
+ const cached = getFromCache(cacheKey, cacheTTL);
83
+ if (cached) {
84
+ logger.debug(`Cache hit for ${cacheKey}`);
85
+ return cached;
86
+ }
87
+ }
88
+ // Try primary URL, then fallbacks
89
+ const urls = [source.primary, ...(source.fallbacks ?? [])];
90
+ let lastError = null;
91
+ for (const url of urls) {
92
+ try {
93
+ const response = await fetchWithRetry(url, retries, retryBackoff);
94
+ const text = await response.text();
95
+ if (cacheKey)
96
+ setCache(cacheKey, text);
97
+ logger.debug(`Loaded ${url} (${text.length} bytes)`);
98
+ return text;
99
+ }
100
+ catch (error) {
101
+ lastError = error instanceof Error ? error : new Error(String(error));
102
+ logger.warn(`Failed to load from ${url}, trying next fallback...`);
103
+ }
104
+ }
105
+ throw lastError ?? new Error('No data sources provided');
106
+ }
107
+ /**
108
+ * Load and parse CSV data from a data source.
109
+ * Uses PapaParse (CSP-compatible) with a simple CSV fallback parser.
110
+ */
111
+ export async function loadCSV(source, options = {}) {
112
+ const text = await loadText(source, options);
113
+ return parseCSV(text);
114
+ }
115
+ /**
116
+ * Load and parse JSON data from a data source.
117
+ */
118
+ export async function loadJSON(source, options = {}) {
119
+ const text = await loadText(source, options);
120
+ return JSON.parse(text);
121
+ }
122
+ /**
123
+ * Parse CSV text into rows.
124
+ * Uses PapaParse if available (CSP-compatible), falls back to a simple CSV parser.
125
+ * Does NOT use d3.csvParse as it requires unsafe-eval in CSP.
126
+ */
127
+ export function parseCSV(text) {
128
+ // Use PapaParse if available (CSP-compatible, no unsafe-eval needed)
129
+ const PapaGlobal = globalThis.Papa;
130
+ if (PapaGlobal?.parse) {
131
+ return PapaGlobal.parse(text, { header: true, skipEmptyLines: true }).data;
132
+ }
133
+ // CSP-safe fallback: simple CSV parser
134
+ const lines = text.trim().split('\n');
135
+ if (lines.length < 2)
136
+ return [];
137
+ const headers = lines[0].split(',').map((h) => h.trim().replace(/^"|"$/g, ''));
138
+ return lines.slice(1).filter((l) => l.trim()).map((line) => {
139
+ const values = line.split(',').map((v) => v.trim().replace(/^"|"$/g, ''));
140
+ const row = {};
141
+ headers.forEach((header, i) => {
142
+ row[header] = values[i] ?? '';
143
+ });
144
+ return row;
145
+ });
146
+ }
147
+ /**
148
+ * Create a DataSource from local path with optional GitHub raw fallback.
149
+ */
150
+ export function createDataSource(localPath, repoPath) {
151
+ const source = { primary: localPath };
152
+ if (repoPath) {
153
+ source.fallbacks = [
154
+ `https://raw.githubusercontent.com/Hack23/cia/master/${repoPath}`,
155
+ ];
156
+ }
157
+ return source;
158
+ }
159
+ //# sourceMappingURL=data-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-loader.js","sourceRoot":"","sources":["../../src/browser/shared/data-loader.ts"],"names":[],"mappings":"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"}