export type PostPreviewDraftPayload = { title: string excerpt: string bodyMarkdown: string visibility: 'private' | 'unlisted' | 'public' } const PREVIEW_DRAFT_KEY_PREFIX = 'post-preview-draft:' const PREVIEW_DRAFT_TTL_MS = 10 * 60 * 1000 type StoredPostPreviewDraftPayload = PostPreviewDraftPayload & { createdAt: number } function isPostPreviewDraftKey(key: string): boolean { return key.startsWith(PREVIEW_DRAFT_KEY_PREFIX) } export function createPostPreviewDraft(payload: PostPreviewDraftPayload): string { const uniqueId = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function' ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(16).slice(2)}` const key = `${PREVIEW_DRAFT_KEY_PREFIX}${uniqueId}` const storedPayload: StoredPostPreviewDraftPayload = { ...payload, createdAt: Date.now(), } localStorage.setItem(key, JSON.stringify(storedPayload)) return uniqueId } export function readPostPreviewDraft(uniqueId: string): PostPreviewDraftPayload | null { const key = `${PREVIEW_DRAFT_KEY_PREFIX}${uniqueId}` const raw = localStorage.getItem(key) if (!raw) { return null } try { const parsed = JSON.parse(raw) as Partial if ( typeof parsed.title !== 'string' || typeof parsed.excerpt !== 'string' || typeof parsed.bodyMarkdown !== 'string' || typeof parsed.createdAt !== 'number' || (parsed.visibility !== 'private' && parsed.visibility !== 'unlisted' && parsed.visibility !== 'public') ) { localStorage.removeItem(key) return null } if (Date.now() - parsed.createdAt > PREVIEW_DRAFT_TTL_MS) { localStorage.removeItem(key) return null } return { title: parsed.title, excerpt: parsed.excerpt, bodyMarkdown: parsed.bodyMarkdown, visibility: parsed.visibility, } } catch { localStorage.removeItem(key) return null } } export function clearPostPreviewDrafts(options?: { onlyExpired?: boolean }) { const onlyExpired = options?.onlyExpired === true const now = Date.now() for (let index = localStorage.length - 1; index >= 0; index -= 1) { const key = localStorage.key(index) if (!key || !isPostPreviewDraftKey(key)) { continue } if (!onlyExpired) { localStorage.removeItem(key) continue } const raw = localStorage.getItem(key) if (!raw) { localStorage.removeItem(key) continue } try { const parsed = JSON.parse(raw) as Partial if (typeof parsed.createdAt !== 'number' || now - parsed.createdAt > PREVIEW_DRAFT_TTL_MS) { localStorage.removeItem(key) } } catch { localStorage.removeItem(key) } } }