riksdagsmonitor 0.9.11 → 0.9.16

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.
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Single source of truth for the canonical schema of every CSV file
3
+ * consumed by a Riksdagsmonitor dashboard.
4
+ *
5
+ * Each entry maps an absolute `/cia-data/...` path to:
6
+ * - `requiredColumns`: column names that MUST appear in the header
7
+ * row. Dashboard code is allowed to read ONLY these columns; any
8
+ * legacy fallback column read is a contract violation.
9
+ * - `dashboard`: human-readable dashboard slug the CSV belongs to
10
+ * (used for grouping in test output).
11
+ * - `minRows`: minimum number of data rows expected (defaults to 1).
12
+ *
13
+ * This contract is enforced from three sides:
14
+ * 1. Build-time vitest contract test
15
+ * (`tests/cia-csv-contracts.test.ts`) walks the real `cia-data/`
16
+ * tree and asserts every contract.
17
+ * 2. Runtime validator (`src/browser/cia/csv-validator.ts`) checks
18
+ * fetched rows against this registry and throws a clear,
19
+ * visible Error so missing columns surface as an error banner
20
+ * instead of a silently-empty chart.
21
+ * 3. Cypress contract spec
22
+ * (`cypress/e2e/dashboards-per-chart/csv-contracts.cy.js`)
23
+ * re-runs the same assertions against the served preview /
24
+ * production site, guarding the deploy pipeline.
25
+ *
26
+ * RULE: No legacy column fallbacks. If a CSV producer changes its
27
+ * schema, update this file (and the dashboard code that reads it) in
28
+ * the same commit. Do NOT add `column_a ?? column_b` fallbacks in
29
+ * dashboard rendering code.
30
+ */
31
+ export interface CsvContract {
32
+ readonly path: string;
33
+ readonly dashboard: string;
34
+ readonly requiredColumns: readonly string[];
35
+ readonly minRows?: number;
36
+ }
37
+ export declare const CSV_CONTRACTS: readonly CsvContract[];
38
+ /**
39
+ * Look up the contract for a given absolute `/cia-data/...` path.
40
+ * Returns `null` when no contract is registered (e.g. an export
41
+ * that is not yet wired into a dashboard).
42
+ */
43
+ export declare function getCsvContract(path: string): CsvContract | null;
44
+ /**
45
+ * All contracts grouped by dashboard slug, useful for iterating
46
+ * per-dashboard in tests.
47
+ */
48
+ export declare function contractsByDashboard(): Record<string, readonly CsvContract[]>;
49
+ //# sourceMappingURL=csv-contracts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv-contracts.d.ts","sourceRoot":"","sources":["../../../src/browser/cia/csv-contracts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAyb/C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAE/D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC,CAO7E"}
@@ -0,0 +1,464 @@
1
+ export const CSV_CONTRACTS = [
2
+ // --------------------------------------------------------------
3
+ // /dashboards/parties.html
4
+ // --------------------------------------------------------------
5
+ {
6
+ path: '/cia-data/party/distribution_party_performance.csv',
7
+ dashboard: 'parties',
8
+ requiredColumns: [
9
+ 'party',
10
+ 'party_name',
11
+ 'active_members',
12
+ 'inactive_members',
13
+ 'documents_last_year',
14
+ 'motions_last_year',
15
+ 'propositions_last_year',
16
+ 'docs_per_member',
17
+ 'performance_level',
18
+ ],
19
+ },
20
+ {
21
+ path: '/cia-data/party/distribution_party_effectiveness_trends.csv',
22
+ dashboard: 'parties',
23
+ requiredColumns: [
24
+ 'party',
25
+ 'year',
26
+ 'quarter',
27
+ 'documents_produced',
28
+ 'motions_count',
29
+ 'active_members',
30
+ 'avg_win_rate',
31
+ 'effectiveness_assessment',
32
+ ],
33
+ },
34
+ {
35
+ path: '/cia-data/party/distribution_party_momentum.csv',
36
+ dashboard: 'parties',
37
+ requiredColumns: [
38
+ 'party',
39
+ 'year',
40
+ 'quarter',
41
+ 'period',
42
+ 'participation_rate',
43
+ 'momentum',
44
+ 'trend_direction',
45
+ 'stability_classification',
46
+ ],
47
+ },
48
+ {
49
+ path: '/cia-data/party/distribution_coalition_alignment.csv',
50
+ dashboard: 'parties',
51
+ requiredColumns: [
52
+ 'party1',
53
+ 'party2',
54
+ 'shared_votes',
55
+ 'aligned_votes',
56
+ 'opposed_votes',
57
+ 'alignment_rate',
58
+ 'coalition_likelihood',
59
+ 'bloc_relationship',
60
+ ],
61
+ },
62
+ {
63
+ path: '/cia-data/party/distribution_annual_party_members.csv',
64
+ dashboard: 'parties',
65
+ requiredColumns: ['year', 'party', 'active_members'],
66
+ },
67
+ // --------------------------------------------------------------
68
+ // /dashboards/coalitions.html
69
+ // --------------------------------------------------------------
70
+ {
71
+ path: '/cia-data/parties/distribution_behavioral_patterns_by_party.csv',
72
+ dashboard: 'coalitions',
73
+ requiredColumns: [
74
+ 'party',
75
+ 'behavioral_assessment',
76
+ 'politician_count',
77
+ 'avg_absence_rate',
78
+ ],
79
+ },
80
+ {
81
+ path: '/cia-data/parties/distribution_decision_patterns_by_party.csv',
82
+ dashboard: 'coalitions',
83
+ requiredColumns: [
84
+ 'party',
85
+ 'committee',
86
+ 'decision_year',
87
+ 'decision_count',
88
+ 'total_decisions',
89
+ 'avg_approval_rate',
90
+ ],
91
+ },
92
+ {
93
+ path: '/cia-data/anomaly/distribution_anomaly_by_party.csv',
94
+ dashboard: 'coalitions',
95
+ requiredColumns: [
96
+ 'party',
97
+ 'anomaly_classification',
98
+ 'politician_count',
99
+ 'avg_rebellions',
100
+ ],
101
+ },
102
+ // --------------------------------------------------------------
103
+ // /dashboards/committees.html
104
+ // --------------------------------------------------------------
105
+ {
106
+ path: '/cia-data/distribution_committee_productivity_matrix.csv',
107
+ dashboard: 'committees',
108
+ requiredColumns: [
109
+ 'committee_code',
110
+ 'committee_name',
111
+ 'year',
112
+ 'quarter',
113
+ 'total_documents',
114
+ 'active_members',
115
+ 'productivity_level',
116
+ 'productivity_assessment',
117
+ ],
118
+ },
119
+ {
120
+ path: '/cia-data/distribution_annual_committee_documents.csv',
121
+ dashboard: 'committees',
122
+ requiredColumns: ['year', 'committee', 'doc_count'],
123
+ },
124
+ // --------------------------------------------------------------
125
+ // /dashboards/election-cycle.html
126
+ // --------------------------------------------------------------
127
+ {
128
+ path: '/cia-data/election-cycle/view_election_cycle_comparative_analysis_sample.csv',
129
+ dashboard: 'election-cycle',
130
+ requiredColumns: [
131
+ 'year',
132
+ 'is_election_year',
133
+ 'total_ballots',
134
+ 'attendance_rate',
135
+ 'avg_yes_rate',
136
+ 'documents_produced',
137
+ 'motions_filed',
138
+ 'proposals_filed',
139
+ 'nearest_election_year',
140
+ 'years_from_election',
141
+ 'election_proximity',
142
+ ],
143
+ },
144
+ {
145
+ path: '/cia-data/election-cycle/view_election_cycle_decision_intelligence_sample.csv',
146
+ dashboard: 'election-cycle',
147
+ requiredColumns: [
148
+ 'election_cycle_id',
149
+ 'cycle_year',
150
+ 'calendar_year',
151
+ 'semester',
152
+ 'party',
153
+ 'total_proposals',
154
+ 'approved_proposals',
155
+ 'rejected_proposals',
156
+ 'avg_approval_rate',
157
+ 'decision_effectiveness',
158
+ 'temporal_approval_rate',
159
+ 'ntile_effectiveness',
160
+ 'decision_trend',
161
+ 'legislative_momentum',
162
+ ],
163
+ },
164
+ {
165
+ path: '/cia-data/election-cycle/view_election_cycle_predictive_intelligence_sample.csv',
166
+ dashboard: 'election-cycle',
167
+ requiredColumns: [
168
+ 'election_cycle_id',
169
+ 'cycle_year',
170
+ 'calendar_year',
171
+ 'semester',
172
+ 'risk_forecast_category',
173
+ 'politicians_at_risk',
174
+ 'avg_risk_score_change',
175
+ 'ministries_at_risk',
176
+ 'forecast_confidence',
177
+ 'predictive_alert_level',
178
+ ],
179
+ },
180
+ {
181
+ path: '/cia-data/election-cycle/view_election_cycle_temporal_trends_sample.csv',
182
+ dashboard: 'election-cycle',
183
+ requiredColumns: [
184
+ 'election_cycle_id',
185
+ 'cycle_year',
186
+ 'calendar_year',
187
+ 'semester',
188
+ 'is_pre_election_semester',
189
+ 'avg_attendance_rate',
190
+ 'total_ballots',
191
+ 'avg_approval_rate',
192
+ 'attendance_trend',
193
+ 'overall_performance_score',
194
+ ],
195
+ },
196
+ // --------------------------------------------------------------
197
+ // /dashboards/seasonal-patterns.html
198
+ // --------------------------------------------------------------
199
+ {
200
+ path: '/cia-data/seasonal/view_riksdagen_seasonal_activity_patterns_sample.csv',
201
+ dashboard: 'seasonal-patterns',
202
+ requiredColumns: [
203
+ 'year',
204
+ 'quarter',
205
+ 'is_election_year',
206
+ 'election_cycle',
207
+ 'total_ballots',
208
+ 'active_politicians',
209
+ 'attendance_rate',
210
+ 'documents_produced',
211
+ 'ballot_z_score',
212
+ 'doc_z_score',
213
+ 'attendance_z_score',
214
+ 'base_activity_classification',
215
+ 'qoq_ballot_change_pct',
216
+ 'seasonal_pattern_classification',
217
+ ],
218
+ },
219
+ // --------------------------------------------------------------
220
+ // /dashboards/pre-election.html
221
+ // --------------------------------------------------------------
222
+ {
223
+ path: '/cia-data/pre-election/view_riksdagen_pre_election_quarterly_activity_sample.csv',
224
+ dashboard: 'pre-election',
225
+ requiredColumns: [
226
+ 'year',
227
+ 'is_election_year',
228
+ 'total_ballots',
229
+ 'avg_attendance_rate',
230
+ 'avg_win_rate',
231
+ 'avg_rebel_rate',
232
+ 'total_documents',
233
+ 'total_proposals',
234
+ 'total_motions',
235
+ 'total_new_assignments',
236
+ 'avg_party_win_rate',
237
+ 'avg_party_absence_rate',
238
+ 'party_documents_total',
239
+ 'baseline_ballots',
240
+ 'baseline_documents',
241
+ 'baseline_assignments',
242
+ 'ballot_deviation_from_baseline',
243
+ 'document_deviation_from_baseline',
244
+ 'assignment_deviation_from_baseline',
245
+ 'ballot_percent_change_from_baseline',
246
+ 'document_percent_change_from_baseline',
247
+ 'ballot_z_score',
248
+ 'document_z_score',
249
+ 'q4_activity_classification',
250
+ 'yoy_ballot_change_pct',
251
+ ],
252
+ },
253
+ {
254
+ path: '/cia-data/pre-election/view_riksdagen_q4_election_year_comparison_sample.csv',
255
+ dashboard: 'pre-election',
256
+ requiredColumns: [
257
+ 'year',
258
+ 'is_election_year',
259
+ 'total_ballots',
260
+ 'attendance_rate',
261
+ 'documents_produced',
262
+ 'ballot_deviation_from_baseline',
263
+ 'doc_deviation_from_baseline',
264
+ 'attendance_deviation_from_baseline',
265
+ 'ballot_percent_change',
266
+ 'doc_percent_change',
267
+ 'q4_pattern',
268
+ 'activity_classification',
269
+ ],
270
+ },
271
+ // --------------------------------------------------------------
272
+ // /dashboards/anomaly-detection.html
273
+ // --------------------------------------------------------------
274
+ {
275
+ path: '/cia-data/seasonal/view_riksdagen_seasonal_anomaly_detection_sample.csv',
276
+ dashboard: 'anomaly-detection',
277
+ requiredColumns: [
278
+ 'year',
279
+ 'quarter',
280
+ 'is_election_year',
281
+ 'total_ballots',
282
+ 'active_politicians',
283
+ 'attendance_rate',
284
+ 'documents_produced',
285
+ 'ballot_z_score',
286
+ 'doc_z_score',
287
+ 'attendance_z_score',
288
+ 'activity_classification',
289
+ 'anomaly_type',
290
+ 'anomaly_direction',
291
+ 'max_z_score',
292
+ 'anomaly_severity',
293
+ ],
294
+ },
295
+ // --------------------------------------------------------------
296
+ // /dashboards/ministers.html
297
+ // --------------------------------------------------------------
298
+ {
299
+ path: '/cia-data/ministry/distribution_ministry_productivity_matrix.csv',
300
+ dashboard: 'ministers',
301
+ requiredColumns: [
302
+ 'ministry_name',
303
+ 'year',
304
+ 'documents_produced',
305
+ 'propositions',
306
+ 'government_bills',
307
+ 'unique_contributors',
308
+ 'performance_assessment',
309
+ ],
310
+ },
311
+ {
312
+ path: '/cia-data/ministry/distribution_ministry_risk_levels.csv',
313
+ dashboard: 'ministers',
314
+ requiredColumns: ['risk_level', 'period_count', 'percentage', 'avg_documents'],
315
+ },
316
+ {
317
+ path: '/cia-data/ministry/distribution_ministry_decision_impact.csv',
318
+ dashboard: 'ministers',
319
+ requiredColumns: [
320
+ 'ministry_code',
321
+ 'committee',
322
+ 'decision_type',
323
+ 'total_proposals',
324
+ 'approved_proposals',
325
+ 'rejected_proposals',
326
+ 'approval_rate',
327
+ ],
328
+ },
329
+ {
330
+ path: '/cia-data/ministry/distribution_ministry_effectiveness.csv',
331
+ dashboard: 'ministers',
332
+ requiredColumns: [
333
+ 'ministry_name',
334
+ 'year',
335
+ 'quarter',
336
+ 'documents_produced',
337
+ 'government_bills',
338
+ 'active_members',
339
+ 'effectiveness_assessment',
340
+ ],
341
+ },
342
+ {
343
+ path: '/cia-data/ministry/distribution_ministry_risk_quarterly.csv',
344
+ dashboard: 'ministers',
345
+ requiredColumns: ['year', 'quarter', 'risk_level', 'ministry_count', 'avg_documents'],
346
+ },
347
+ {
348
+ path: '/cia-data/politician/view_riksdagen_politician_influence_metrics_sample.csv',
349
+ dashboard: 'ministers',
350
+ requiredColumns: [
351
+ 'person_id',
352
+ 'first_name',
353
+ 'last_name',
354
+ 'party',
355
+ 'network_connections',
356
+ 'network_median',
357
+ 'influence_classification',
358
+ 'broker_classification',
359
+ 'influence_assessment',
360
+ ],
361
+ },
362
+ {
363
+ path: '/cia-data/ministry/view_ministry_productivity_matrix_sample.csv',
364
+ dashboard: 'ministers',
365
+ requiredColumns: [
366
+ 'org_code',
367
+ 'name',
368
+ 'year',
369
+ 'documents_produced',
370
+ 'propositions',
371
+ 'government_bills',
372
+ 'unique_contributors',
373
+ 'avg_documents',
374
+ 'productivity_quartile',
375
+ 'performance_assessment',
376
+ ],
377
+ },
378
+ {
379
+ path: '/cia-data/ministry/view_ministry_risk_evolution_sample.csv',
380
+ dashboard: 'ministers',
381
+ requiredColumns: [
382
+ 'org_code',
383
+ 'name',
384
+ 'assessment_period',
385
+ 'year',
386
+ 'quarter',
387
+ 'documents_produced',
388
+ 'legislative_count',
389
+ 'active_members',
390
+ 'document_trend',
391
+ 'risk_level',
392
+ 'risk_assessment',
393
+ ],
394
+ },
395
+ // --------------------------------------------------------------
396
+ // /dashboards/risk.html
397
+ // --------------------------------------------------------------
398
+ {
399
+ path: '/cia-data/politician/view_politician_risk_summary_sample.csv',
400
+ dashboard: 'risk',
401
+ requiredColumns: [
402
+ 'person_id',
403
+ 'first_name',
404
+ 'last_name',
405
+ 'party',
406
+ 'status',
407
+ 'total_violations',
408
+ 'risk_score',
409
+ 'risk_level',
410
+ 'risk_assessment',
411
+ ],
412
+ },
413
+ // --------------------------------------------------------------
414
+ // /dashboard/index.html — CIA hub
415
+ // --------------------------------------------------------------
416
+ {
417
+ path: '/cia-data/election/election_forecast.csv',
418
+ dashboard: 'cia-hub',
419
+ requiredColumns: [
420
+ 'id',
421
+ 'name',
422
+ 'currentSeats',
423
+ 'predictedSeats',
424
+ 'change',
425
+ 'voteShare',
426
+ 'confidenceMin',
427
+ 'confidenceMax',
428
+ ],
429
+ },
430
+ {
431
+ path: '/cia-data/election/coalition_scenarios.csv',
432
+ dashboard: 'cia-hub',
433
+ requiredColumns: [
434
+ 'name',
435
+ 'probability',
436
+ 'composition',
437
+ 'totalSeats',
438
+ 'majority',
439
+ 'riskLevel',
440
+ ],
441
+ },
442
+ ];
443
+ /**
444
+ * Look up the contract for a given absolute `/cia-data/...` path.
445
+ * Returns `null` when no contract is registered (e.g. an export
446
+ * that is not yet wired into a dashboard).
447
+ */
448
+ export function getCsvContract(path) {
449
+ return CSV_CONTRACTS.find((c) => c.path === path) ?? null;
450
+ }
451
+ /**
452
+ * All contracts grouped by dashboard slug, useful for iterating
453
+ * per-dashboard in tests.
454
+ */
455
+ export function contractsByDashboard() {
456
+ const out = {};
457
+ for (const c of CSV_CONTRACTS) {
458
+ if (!out[c.dashboard])
459
+ out[c.dashboard] = [];
460
+ out[c.dashboard].push(c);
461
+ }
462
+ return out;
463
+ }
464
+ //# sourceMappingURL=csv-contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv-contracts.js","sourceRoot":"","sources":["../../../src/browser/cia/csv-contracts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Runtime CSV column validator.
3
+ *
4
+ * Used by dashboard data-loaders right after a CSV is parsed.
5
+ * If the parsed rows are missing any of the columns declared in the
6
+ * registered `CsvContract` for a given path, this module throws a
7
+ * clear, structured error. Dashboard error handlers surface the
8
+ * thrown message in their visible error banner, so a CSV schema
9
+ * regression is impossible to ship as a silently-empty chart.
10
+ *
11
+ * Validation rules (intentionally strict — no legacy fallbacks):
12
+ * 1. Header check: every name in `contract.requiredColumns` must
13
+ * appear in the row keys of the first parsed record.
14
+ * 2. Row count: `rows.length >= contract.minRows ?? 1`.
15
+ * 3. No partial rows: a row missing one of the required column
16
+ * keys (i.e. the parser lost a column) fails the check.
17
+ *
18
+ * Use `validateCsvRows()` from any dashboard data loader. Use
19
+ * `validateCsvRowsLenient()` when the CSV is best-effort (e.g. an
20
+ * unreleased export pulled from a remote CDN); it only logs to
21
+ * `console.error` instead of throwing.
22
+ */
23
+ import { type CsvContract } from './csv-contracts';
24
+ export declare class CsvContractError extends Error {
25
+ readonly path: string;
26
+ readonly missingColumns: readonly string[];
27
+ readonly availableColumns: readonly string[];
28
+ readonly rowCount: number;
29
+ constructor(path: string, missingColumns: readonly string[], availableColumns: readonly string[], rowCount: number);
30
+ }
31
+ type CsvRowLike = Readonly<Record<string, unknown>>;
32
+ /**
33
+ * Inspect parsed rows against the registered contract for `path`.
34
+ * Returns the input unchanged on success. Throws `CsvContractError`
35
+ * on any violation. If no contract is registered for `path`,
36
+ * validation is skipped (returns input).
37
+ */
38
+ export declare function validateCsvRows<T extends CsvRowLike>(path: string, rows: readonly T[], overrideContract?: CsvContract): readonly T[];
39
+ /**
40
+ * Same checks, but logs to `console.error` instead of throwing.
41
+ * Returns `true` when the contract is satisfied (or no contract is
42
+ * registered), `false` otherwise.
43
+ */
44
+ export declare function validateCsvRowsLenient<T extends CsvRowLike>(path: string, rows: readonly T[]): boolean;
45
+ export {};
46
+ //# sourceMappingURL=csv-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv-validator.d.ts","sourceRoot":"","sources":["../../../src/browser/cia/csv-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,KAAK,WAAW,EAAkB,MAAM,iBAAiB,CAAC;AAEnE,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAExB,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,SAAS,MAAM,EAAE,EACjC,gBAAgB,EAAE,SAAS,MAAM,EAAE,EACnC,QAAQ,EAAE,MAAM;CAanB;AAED,KAAK,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEpD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,UAAU,EAClD,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,CAAC,EAAE,EAClB,gBAAgB,CAAC,EAAE,WAAW,GAC7B,SAAS,CAAC,EAAE,CAqBd;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,UAAU,EACzD,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,CAAC,EAAE,GACjB,OAAO,CAWT"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Runtime CSV column validator.
3
+ *
4
+ * Used by dashboard data-loaders right after a CSV is parsed.
5
+ * If the parsed rows are missing any of the columns declared in the
6
+ * registered `CsvContract` for a given path, this module throws a
7
+ * clear, structured error. Dashboard error handlers surface the
8
+ * thrown message in their visible error banner, so a CSV schema
9
+ * regression is impossible to ship as a silently-empty chart.
10
+ *
11
+ * Validation rules (intentionally strict — no legacy fallbacks):
12
+ * 1. Header check: every name in `contract.requiredColumns` must
13
+ * appear in the row keys of the first parsed record.
14
+ * 2. Row count: `rows.length >= contract.minRows ?? 1`.
15
+ * 3. No partial rows: a row missing one of the required column
16
+ * keys (i.e. the parser lost a column) fails the check.
17
+ *
18
+ * Use `validateCsvRows()` from any dashboard data loader. Use
19
+ * `validateCsvRowsLenient()` when the CSV is best-effort (e.g. an
20
+ * unreleased export pulled from a remote CDN); it only logs to
21
+ * `console.error` instead of throwing.
22
+ */
23
+ import { getCsvContract } from './csv-contracts';
24
+ export class CsvContractError extends Error {
25
+ path;
26
+ missingColumns;
27
+ availableColumns;
28
+ rowCount;
29
+ constructor(path, missingColumns, availableColumns, rowCount) {
30
+ super(`CSV ${path} fails its registered contract — missing columns: [${missingColumns.join(', ')}]; received ${rowCount} row(s); header: [${availableColumns.join(', ')}]`);
31
+ this.name = 'CsvContractError';
32
+ this.path = path;
33
+ this.missingColumns = missingColumns;
34
+ this.availableColumns = availableColumns;
35
+ this.rowCount = rowCount;
36
+ }
37
+ }
38
+ /**
39
+ * Inspect parsed rows against the registered contract for `path`.
40
+ * Returns the input unchanged on success. Throws `CsvContractError`
41
+ * on any violation. If no contract is registered for `path`,
42
+ * validation is skipped (returns input).
43
+ */
44
+ export function validateCsvRows(path, rows, overrideContract) {
45
+ const contract = overrideContract ?? getCsvContract(path);
46
+ if (!contract)
47
+ return rows;
48
+ const minRows = contract.minRows ?? 1;
49
+ if (rows.length < minRows) {
50
+ throw new CsvContractError(path, contract.requiredColumns, rows[0] ? Object.keys(rows[0]) : [], rows.length);
51
+ }
52
+ const headerKeys = Object.keys(rows[0]);
53
+ const headerSet = new Set(headerKeys);
54
+ const missing = contract.requiredColumns.filter((c) => !headerSet.has(c));
55
+ if (missing.length > 0) {
56
+ throw new CsvContractError(path, missing, headerKeys, rows.length);
57
+ }
58
+ return rows;
59
+ }
60
+ /**
61
+ * Same checks, but logs to `console.error` instead of throwing.
62
+ * Returns `true` when the contract is satisfied (or no contract is
63
+ * registered), `false` otherwise.
64
+ */
65
+ export function validateCsvRowsLenient(path, rows) {
66
+ try {
67
+ validateCsvRows(path, rows);
68
+ return true;
69
+ }
70
+ catch (err) {
71
+ if (err instanceof CsvContractError) {
72
+ console.error('[csv-validator]', err.message);
73
+ return false;
74
+ }
75
+ throw err;
76
+ }
77
+ }
78
+ //# sourceMappingURL=csv-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv-validator.js","sourceRoot":"","sources":["../../../src/browser/cia/csv-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAoB,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEnE,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,IAAI,CAAS;IACb,cAAc,CAAoB;IAClC,gBAAgB,CAAoB;IACpC,QAAQ,CAAS;IAC1B,YACE,IAAY,EACZ,cAAiC,EACjC,gBAAmC,EACnC,QAAgB;QAEhB,KAAK,CACH,OAAO,IAAI,sDACT,cAAc,CAAC,IAAI,CAAC,IAAI,CAC1B,eAAe,QAAQ,qBAAqB,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC3E,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;QACzC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAID;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAY,EACZ,IAAkB,EAClB,gBAA8B;IAE9B,MAAM,QAAQ,GAAG,gBAAgB,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC;IAC1D,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3B,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;IACtC,IAAI,IAAI,CAAC,MAAM,GAAG,OAAO,EAAE,CAAC;QAC1B,MAAM,IAAI,gBAAgB,CACxB,IAAI,EACJ,QAAQ,CAAC,eAAe,EACxB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EACnC,IAAI,CAAC,MAAM,CACZ,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAY,EACZ,IAAkB;IAElB,IAAI,CAAC;QACH,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,gBAAgB,EAAE,CAAC;YACpC,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YAC9C,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}