All files / scripts/political-intelligence daily-streams.ts

79.16% Statements 38/48
54.54% Branches 12/22
92.85% Functions 13/14
81.39% Lines 35/43

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124                                            1x 1x   1x 1x                                                                           2490x 2490x 2490x 94365x 59445x 59445x 59445x 2040x 57405x 57405x               2490x 54975x       15x 15x 15x 510x 480x 465x   15x 480x 480x 3465x 2490x 2745x   480x 2490x 2490x 2490x               2490x   480x               15x    
/**
 * @module Infrastructure/PoliticalIntelligence/DailyStreams
 * @category Intelligence Operations / Supporting Infrastructure
 * @name Daily-stream artifact discovery
 *
 * @description
 * Walks `analysis/daily/<YYYY-MM-DD>/<stream>/` to produce a structured
 * `DailyDay[]` with one entry per date and one `DailyStream` per workflow
 * stream. Recursively counts `.md` / `.json` artifacts. Sorted newest-first.
 *
 * Round-6 split: extracted from `scripts/generate-political-intelligence.ts`.
 *
 * @author Hack23 AB (Infrastructure Team)
 * @license Apache-2.0
 */
 
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
 
import { buildGithubUrl } from './catalog.js';
 
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
 
const ROOT_DIR = path.join(__dirname, '..', '..');
const DAILY_DIR = path.join(ROOT_DIR, 'analysis', 'daily');
 
export interface DailyArtifact {
  readonly file: string;
  /** Path relative to analysis/daily (e.g. 2026-04-23/propositions/executive-brief.md). */
  readonly relative: string;
  readonly githubUrl: string;
}
 
export interface DailyStream {
  readonly name: string;
  readonly githubUrl: string;
  readonly artifactCount: number;
  readonly artifacts: DailyArtifact[];
}
 
export interface DailyDay {
  readonly date: string;
  readonly githubUrl: string;
  readonly streams: DailyStream[];
  readonly totalArtifacts: number;
}
 
export function countArtifactsRecursive(dir: string): number {
  let n = 0;
  for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
    const full = path.join(dir, e.name);
    if (e.isDirectory()) {
      n += countArtifactsRecursive(full);
    } else if (e.isFile() && /\.(md|json)$/i.test(e.name)) {
      n += 1;
    }
  }
  return n;
}
 
/** Recursively collect every `.md`/`.json` artifact under a stream folder, sorted by relative path. */
export function collectStreamArtifacts(streamDir: string, streamRelativeBase: string): DailyArtifact[] {
  Iif (!fs.existsSync(streamDir)) return [];
  const out: DailyArtifact[] = [];
  const walk = (dir: string, relBase: string): void => {
    for (const e of fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
      const full = path.join(dir, e.name);
      const rel = relBase ? `${relBase}/${e.name}` : e.name;
      if (e.isDirectory()) {
        walk(full, rel);
      } else if (Ee.isFile() && /\.(md|json)$/i.test(e.name)) {
        out.push({
          file: rel,
          relative: `${streamRelativeBase}/${rel}`,
          githubUrl: buildGithubUrl('blob', `analysis/daily/${streamRelativeBase}/${rel}`),
        });
      }
    }
  };
  walk(streamDir, '');
  return out.sort((a, b) => a.file.localeCompare(b.file));
}
 
export function collectDailyDays(): DailyDay[] {
  Iif (!fs.existsSync(DAILY_DIR)) return [];
  const days: DailyDay[] = [];
  const dateEntries = fs.readdirSync(DAILY_DIR, { withFileTypes: true })
    .filter((e) => e.isDirectory() && /^\d{4}-\d{2}-\d{2}$/.test(e.name))
    .map((e) => e.name)
    .sort((a, b) => b.localeCompare(a)); // newest first
 
  for (const date of dateEntries) {
    const dateDir = path.join(DAILY_DIR, date);
    const streamNames = fs.readdirSync(dateDir, { withFileTypes: true })
      .filter((e) => e.isDirectory())
      .map((e) => e.name)
      .sort((a, b) => a.localeCompare(b));
 
    const streams: DailyStream[] = streamNames.map((name) => {
      const streamDir = path.join(dateDir, name);
      const artifacts = collectStreamArtifacts(streamDir, `${date}/${name}`);
      return {
        name,
        githubUrl: buildGithubUrl('tree', `analysis/daily/${date}/${name}`),
        artifactCount: artifacts.length || countArtifactsRecursive(streamDir),
        artifacts,
      };
    });
 
    const totalArtifacts = streams.reduce((a, s) => a + s.artifactCount, 0);
 
    days.push({
      date,
      githubUrl: buildGithubUrl('tree', `analysis/daily/${date}`),
      streams,
      totalArtifacts,
    });
  }
 
  return days;
}