66 lines
1.8 KiB
TypeScript
66 lines
1.8 KiB
TypeScript
// @generated — DO NOT EDIT. Source: packages/shared/draft.ts
|
|
/**
|
|
* Draft Storage
|
|
*
|
|
* Persists annotation drafts to ~/.plannotator/drafts/ so they survive
|
|
* server crashes. Each draft is keyed by a content hash of the plan/diff
|
|
* it was created against.
|
|
*
|
|
* Runtime-agnostic: uses only node:fs, node:path, node:os, node:crypto.
|
|
*/
|
|
|
|
import { homedir } from "os";
|
|
import { join } from "path";
|
|
import { mkdirSync, writeFileSync, readFileSync, unlinkSync, existsSync } from "fs";
|
|
import { createHash } from "crypto";
|
|
|
|
/**
|
|
* Get the drafts directory, creating it if needed.
|
|
*/
|
|
export function getDraftDir(): string {
|
|
const dir = join(homedir(), ".plannotator", "drafts");
|
|
mkdirSync(dir, { recursive: true });
|
|
return dir;
|
|
}
|
|
|
|
/**
|
|
* Generate a stable key from content using truncated SHA-256.
|
|
* Same content always produces the same key across server restarts.
|
|
*/
|
|
export function contentHash(content: string): string {
|
|
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
}
|
|
|
|
/**
|
|
* Save a draft to disk.
|
|
*/
|
|
export function saveDraft(key: string, data: object): void {
|
|
const dir = getDraftDir();
|
|
writeFileSync(join(dir, `${key}.json`), JSON.stringify(data), "utf-8");
|
|
}
|
|
|
|
/**
|
|
* Load a draft from disk. Returns null if not found.
|
|
*/
|
|
export function loadDraft(key: string): object | null {
|
|
const filePath = join(getDraftDir(), `${key}.json`);
|
|
try {
|
|
if (!existsSync(filePath)) return null;
|
|
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a draft from disk. No-op if not found.
|
|
*/
|
|
export function deleteDraft(key: string): void {
|
|
const filePath = join(getDraftDir(), `${key}.json`);
|
|
try {
|
|
if (existsSync(filePath)) unlinkSync(filePath);
|
|
} catch {
|
|
// Ignore delete failures
|
|
}
|
|
}
|