@trazum/cli 1.46.0 → 1.48.0

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,81 @@
1
+ /**
2
+ * Where a waiver's uses are written down.
3
+ *
4
+ * The core decides what a use means and what a run of them adds up to; this
5
+ * decides where the bytes go — the same split every module here follows, so
6
+ * `@trazum/core` stays browser-safe and the CLI keeps its monopoly on I/O.
7
+ *
8
+ * **Append-only, and never rewritten.** There is deliberately no prune, no
9
+ * compaction and no `--clear`: a record of decisions that the tool can erase
10
+ * is a record nobody can rely on, and the one thing a waiver history is for is
11
+ * being awkward six months later. Deleting the file is a thing a person does
12
+ * with `rm`, on purpose, having seen it.
13
+ *
14
+ * **A write that fails never fails the run.** The gate's job is the exit code.
15
+ * A read-only checkout, a full disk or a directory somebody's CI cannot create
16
+ * must not turn a passing build red on account of bookkeeping — the failure is
17
+ * reported and the gate's own verdict stands.
18
+ *
19
+ * **A line that will not parse is counted and skipped**, exactly as in the
20
+ * store. Losing the whole history because one line is broken would be the
21
+ * worst possible response; pretending the history is complete would be the
22
+ * second worst.
23
+ */
24
+
25
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
26
+ import { join } from 'node:path';
27
+ import { isWaiverUse } from '@trazum/core';
28
+ import type { WaiverUse } from '@trazum/core';
29
+
30
+ /** One file, not one per month: a waiver history is small and read whole. */
31
+ export const WAIVER_LOG = '.trazum/waivers.jsonl';
32
+
33
+ export interface WaiverReadResult {
34
+ uses: WaiverUse[];
35
+ /** 1-based positions of lines that would not parse. Named, never dropped quietly. */
36
+ unreadable: number[];
37
+ /** False when the file does not exist — "nothing recorded" is not "no file". */
38
+ present: boolean;
39
+ }
40
+
41
+ export async function readWaiverLog(root: string): Promise<WaiverReadResult> {
42
+ let raw: string;
43
+ try {
44
+ raw = await readFile(join(root, WAIVER_LOG), 'utf8');
45
+ } catch {
46
+ // Absent is the normal state of a repository that has never waived
47
+ // anything, and it is not an error.
48
+ return { uses: [], unreadable: [], present: false };
49
+ }
50
+
51
+ const uses: WaiverUse[] = [];
52
+ const unreadable: number[] = [];
53
+ raw.split('\n').forEach((line, index) => {
54
+ if (line.trim() === '') return;
55
+ try {
56
+ const parsed: unknown = JSON.parse(line);
57
+ if (isWaiverUse(parsed)) uses.push(parsed);
58
+ else unreadable.push(index + 1);
59
+ } catch {
60
+ unreadable.push(index + 1);
61
+ }
62
+ });
63
+ return { uses, unreadable, present: true };
64
+ }
65
+
66
+ /**
67
+ * Appends one use, and swallows any failure after reporting it.
68
+ *
69
+ * Returns the error message rather than throwing, so the caller can print it
70
+ * beside the gate's own output without the gate ever depending on the write.
71
+ */
72
+ export async function appendWaiverUse(root: string, use: WaiverUse): Promise<string | null> {
73
+ const path = join(root, WAIVER_LOG);
74
+ try {
75
+ await mkdir(join(path, '..'), { recursive: true });
76
+ await writeFile(path, `${JSON.stringify(use)}\n`, { flag: 'a', mode: 0o600 });
77
+ return null;
78
+ } catch (error) {
79
+ return error instanceof Error ? error.message : String(error);
80
+ }
81
+ }