okr/scripts/arkivklar.mjs

86 lines
3.7 KiB
JavaScript

#!/usr/bin/env node
// arkivklar.mjs
// D7 steg 18: orkestrator for /okr:arkivklar. Leser en OKF-bundle og skriver et
// VURDERINGSGRUNNLAG mot bevaringsforskrifta -- aldri en avgjoerelse.
//
// Arbeidsdelingen foelger D6-beslutning 1: lib/arkivklar.mjs eier traversering,
// klassifisering og rendring; dette skriptet eier disk-siden (hvor rota ligger,
// hvor rapporten havner) og CLI-kontrakten.
//
// STDOUT ER DEFAULT, fil er opt-in. Kommandoen (/okr:arkivklar) deklarerer
// verken Write eller Edit, og en verify-kjoering mot tests/fixtures/ skal ikke
// kunne skitne til fixturen. Oppgis en rapport-fil, skrives den ATOMISK
// (temp i samme katalog + renameSync) og ALDRI inn i bundlen selv.
//
// INGEN SLETTEMODELL FINNES HER, og ingen skal innfoeres. Retensjonsbehovet er
// dekket av /okr:oppsett arkiver (arkivering av syklus) og /okr:export (uttrekk).
// Kassasjon krever Nasjonalarkivets hjemmel etter arkivlova § 13 og er ikke en
// operasjon dette verktoeyet kan ha.
//
// Exit: 0 = rapport produsert · 1 = uventet feil (inkl. confinement-brudd)
// 2 = bruksfeil (manglende/ukjent sti)
import { writeFileSync, renameSync, existsSync, statSync, realpathSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { traverserBundle, rapport } from '../lib/arkivklar.mjs';
// Atomisk: temp-fil i SAMME katalog, deretter renameSync over maalet.
// Speiler write-org-profile.mjs:34-40 / innboks-write.mjs.
function writeAtomic(target, data) {
const dir = path.dirname(target);
const tmp = path.join(dir, `${path.basename(target)}.${process.pid}.tmp`);
writeFileSync(tmp, data);
renameSync(tmp, target);
}
export function byggRapport(bundleRoot, opts) {
return rapport(traverserBundle(bundleRoot), opts);
}
// --- CLI ---
const isMain = process.argv[1]
&& fileURLToPath(import.meta.url) === process.argv[1];
if (isMain) {
const [bundleRoot, rapportFil] = process.argv.slice(2);
if (!bundleRoot) {
process.stderr.write('Bruk: node arkivklar.mjs <bundle-rot> [rapport-fil]\n');
process.exit(2);
}
if (!existsSync(bundleRoot) || !statSync(bundleRoot).isDirectory()) {
process.stderr.write(`Finnes ikke (eller er ikke en katalog): ${bundleRoot}\n`);
process.exit(2);
}
try {
const ut = byggRapport(bundleRoot);
if (rapportFil) {
// Haandhever loeftet i headeren: rapporten skal ALDRI havne i bundlen den
// vurderer. En rapport i treet ville blitt klassifisert av neste kjoering
// (selv-forurensning), og et loefte som bare staar i prosa er ikke et
// loefte -- samme standard som tool-lista i commands/arkivklar.md.
//
// BEGGE sider maa realpath-es, ellers er sammenligningen verdiloes: paa
// macOS er /var en symlenke til /private/var, saa en realpath-et rot mot
// en bare resolvet maal-sti sammenligner to skrivemaater av samme sted og
// slipper alt gjennom. Maalet finnes ikke ennaa, saa det er FORELDRE-
// katalogen som realpath-es -- samme grep som writeConfined i
// lib/innboks-write.mjs.
const maalDir = realpathSync(path.dirname(path.resolve(rapportFil)));
const maal = path.join(maalDir, path.basename(rapportFil));
const rot = realpathSync(path.resolve(bundleRoot));
if (maal === rot || maal.startsWith(rot + path.sep)) {
process.stderr.write(`arkivklar: rapporten kan ikke skrives inne i bundlen som vurderes: ${rapportFil}\n`);
process.exit(1);
}
writeAtomic(maal, ut);
process.stdout.write(`Arkivklar: vurderingsgrunnlag skrevet til ${rapportFil}\n`);
} else {
process.stdout.write(ut);
}
process.exit(0);
} catch (e) {
process.stderr.write(`arkivklar: ${e.message}\n`);
process.exit(1);
}
}