@greenarmor/ges-core
Advanced tools
| /** | ||
| * Writes JSON to disk atomically using write-to-temp-then-rename. | ||
| * | ||
| * On POSIX systems, `fs.renameSync()` is atomic — the file is either | ||
| * the old version or the new version, never half-written. This prevents | ||
| * corruption if the process crashes mid-write (power loss, OOM, SIGKILL). | ||
| * | ||
| * The temp file is created in the same directory as the target to ensure | ||
| * the rename operation is atomic (cross-device renames are not atomic). | ||
| */ | ||
| export declare function safeWriteJson(filePath: string, data: unknown, indent?: number): void; | ||
| /** | ||
| * Writes a string to disk atomically using write-to-temp-then-rename. | ||
| * | ||
| * Creates parent directories if they don't exist. | ||
| * Uses `.tmp` extension for the intermediate file, cleaned up on error. | ||
| */ | ||
| export declare function safeWriteFile(filePath: string, content: string, _encoding?: BufferEncoding): void; | ||
| /** | ||
| * Reads and parses a JSON file with error handling. | ||
| * Returns the fallback value if the file doesn't exist or is malformed. | ||
| */ | ||
| export declare function safeReadJson<T>(filePath: string, fallback: T): T; |
| import * as fs from "node:fs"; | ||
| import * as path from "node:path"; | ||
| /** | ||
| * Writes JSON to disk atomically using write-to-temp-then-rename. | ||
| * | ||
| * On POSIX systems, `fs.renameSync()` is atomic — the file is either | ||
| * the old version or the new version, never half-written. This prevents | ||
| * corruption if the process crashes mid-write (power loss, OOM, SIGKILL). | ||
| * | ||
| * The temp file is created in the same directory as the target to ensure | ||
| * the rename operation is atomic (cross-device renames are not atomic). | ||
| */ | ||
| export function safeWriteJson(filePath, data, indent = 2) { | ||
| const json = JSON.stringify(data, null, indent); | ||
| safeWriteFile(filePath, json); | ||
| } | ||
| /** | ||
| * Writes a string to disk atomically using write-to-temp-then-rename. | ||
| * | ||
| * Creates parent directories if they don't exist. | ||
| * Uses `.tmp` extension for the intermediate file, cleaned up on error. | ||
| */ | ||
| export function safeWriteFile(filePath, content, _encoding = "utf-8") { | ||
| const dir = path.dirname(filePath); | ||
| if (!fs.existsSync(dir)) { | ||
| fs.mkdirSync(dir, { recursive: true }); | ||
| } | ||
| const tmpPath = filePath + ".tmp"; | ||
| try { | ||
| fs.writeFileSync(tmpPath, content, "utf-8"); | ||
| fs.renameSync(tmpPath, filePath); | ||
| } | ||
| catch (err) { | ||
| // Clean up temp file if rename failed | ||
| try { | ||
| if (fs.existsSync(tmpPath)) { | ||
| fs.unlinkSync(tmpPath); | ||
| } | ||
| } | ||
| catch { | ||
| // ignore cleanup errors | ||
| } | ||
| throw err; | ||
| } | ||
| } | ||
| /** | ||
| * Reads and parses a JSON file with error handling. | ||
| * Returns the fallback value if the file doesn't exist or is malformed. | ||
| */ | ||
| export function safeReadJson(filePath, fallback) { | ||
| try { | ||
| const raw = fs.readFileSync(filePath, "utf-8"); | ||
| const data = JSON.parse(raw); | ||
| return data; | ||
| } | ||
| catch { | ||
| return fallback; | ||
| } | ||
| } |
| import * as fs from "node:fs"; | ||
| import * as path from "node:path"; | ||
| import { safeWriteJson } from "../utils/index.js"; | ||
| export function loadActivityLog(projectPath) { | ||
@@ -17,10 +18,6 @@ const logPath = path.join(projectPath, ".ges", "activity-log.json"); | ||
| return; | ||
| const gesDir = path.join(projectPath, ".ges"); | ||
| if (!fs.existsSync(gesDir)) { | ||
| fs.mkdirSync(gesDir, { recursive: true }); | ||
| } | ||
| const logPath = path.join(gesDir, "activity-log.json"); | ||
| const logPath = path.join(projectPath, ".ges", "activity-log.json"); | ||
| const existing = loadActivityLog(projectPath); | ||
| const updated = existing.concat(entries); | ||
| fs.writeFileSync(logPath, JSON.stringify(updated, null, 2), "utf-8"); | ||
| safeWriteJson(logPath, updated); | ||
| } | ||
@@ -27,0 +24,0 @@ export function clearActivityLog(projectPath) { |
| import * as fs from "node:fs"; | ||
| import * as path from "node:path"; | ||
| import { safeWriteJson } from "../utils/index.js"; | ||
| const GES_DIR = ".ges"; | ||
@@ -70,6 +71,2 @@ const CONTROLS_DIR = "controls"; | ||
| export function saveControlOverride(projectPath, controlId, status, reason) { | ||
| const gesDir = path.join(projectPath, GES_DIR); | ||
| if (!fs.existsSync(gesDir)) { | ||
| fs.mkdirSync(gesDir, { recursive: true }); | ||
| } | ||
| const overrides = loadControlOverrides(projectPath); | ||
@@ -84,4 +81,4 @@ const existingIdx = overrides.findIndex(o => o.control_id === controlId); | ||
| } | ||
| const overridesPath = path.join(gesDir, OVERRIDES_FILE); | ||
| fs.writeFileSync(overridesPath, JSON.stringify(overrides, null, 2), "utf-8"); | ||
| const overridesPath = path.join(projectPath, GES_DIR, OVERRIDES_FILE); | ||
| safeWriteJson(overridesPath, overrides); | ||
| } | ||
@@ -123,3 +120,3 @@ export function applyOverridesToControls(controls, overrides) { | ||
| config.frameworks.push(framework); | ||
| fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8"); | ||
| safeWriteJson(configPath, config); | ||
| return true; | ||
@@ -142,3 +139,3 @@ } | ||
| return false; | ||
| fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8"); | ||
| safeWriteJson(configPath, config); | ||
| return true; | ||
@@ -145,0 +142,0 @@ } |
| import * as fs from "node:fs"; | ||
| import * as path from "node:path"; | ||
| import { safeWriteJson } from "../utils/index.js"; | ||
| const ASSIGNMENTS_FILE = "fix-assignments.json"; | ||
@@ -19,7 +20,3 @@ function assignmentsPath(projectPath) { | ||
| export function saveFixAssignments(projectPath, assignments) { | ||
| const gesDir = path.join(projectPath, ".ges"); | ||
| if (!fs.existsSync(gesDir)) { | ||
| fs.mkdirSync(gesDir, { recursive: true }); | ||
| } | ||
| fs.writeFileSync(assignmentsPath(projectPath), JSON.stringify(assignments, null, 2), "utf-8"); | ||
| safeWriteJson(assignmentsPath(projectPath), assignments); | ||
| } | ||
@@ -26,0 +23,0 @@ let assignmentCounter = 0; |
| import * as fs from "node:fs"; | ||
| import * as path from "node:path"; | ||
| import { safeWriteJson } from "../utils/index.js"; | ||
| export function loadFixHistory(projectPath) { | ||
@@ -17,10 +18,6 @@ const histPath = path.join(projectPath, ".ges", "fix-history.json"); | ||
| return; | ||
| const gesDir = path.join(projectPath, ".ges"); | ||
| if (!fs.existsSync(gesDir)) { | ||
| fs.mkdirSync(gesDir, { recursive: true }); | ||
| } | ||
| const histPath = path.join(gesDir, "fix-history.json"); | ||
| const histPath = path.join(projectPath, ".ges", "fix-history.json"); | ||
| const existing = loadFixHistory(projectPath); | ||
| const updated = existing.concat(entries); | ||
| fs.writeFileSync(histPath, JSON.stringify(updated, null, 2), "utf-8"); | ||
| safeWriteJson(histPath, updated); | ||
| } | ||
@@ -27,0 +24,0 @@ export function clearFixHistory(projectPath) { |
| import * as fs from "node:fs"; | ||
| import * as path from "node:path"; | ||
| import { safeWriteJson } from "../utils/index.js"; | ||
| const GOVERNANCE_FILE = "governance-records.json"; | ||
@@ -19,7 +20,3 @@ function recordsPath(projectPath) { | ||
| export function saveGovernanceRecords(projectPath, records) { | ||
| const gesDir = path.join(projectPath, ".ges"); | ||
| if (!fs.existsSync(gesDir)) { | ||
| fs.mkdirSync(gesDir, { recursive: true }); | ||
| } | ||
| fs.writeFileSync(recordsPath(projectPath), JSON.stringify(records, null, 2), "utf-8"); | ||
| safeWriteJson(recordsPath(projectPath), records); | ||
| } | ||
@@ -26,0 +23,0 @@ let govCounter = 0; |
+1
-0
@@ -10,1 +10,2 @@ export * from "./types/index.js"; | ||
| export * from "./fix-assignments/index.js"; | ||
| export * from "./utils/index.js"; |
+1
-0
@@ -10,1 +10,2 @@ export * from "./types/index.js"; | ||
| export * from "./fix-assignments/index.js"; | ||
| export * from "./utils/index.js"; |
| import * as fs from "node:fs"; | ||
| import * as path from "node:path"; | ||
| import { safeWriteFile } from "../utils/index.js"; | ||
| let recCounter = 0; | ||
@@ -61,3 +62,3 @@ export function recordAIRecommendation(projectPath, opts) { | ||
| md.push(""); | ||
| fs.writeFileSync(path.join(devLogsDir, fileName), md.join("\n"), "utf-8"); | ||
| safeWriteFile(path.join(devLogsDir, fileName), md.join("\n")); | ||
| return recommendation; | ||
@@ -64,0 +65,0 @@ } |
+1
-1
@@ -27,3 +27,3 @@ { | ||
| "types": "./dist/index.d.ts", | ||
| "version": "1.5.1", | ||
| "version": "1.5.2", | ||
| "scripts": { | ||
@@ -30,0 +30,0 @@ "build": "tsc", |
92379
2.61%25
8.7%2289
3.15%