123456789101112131415161718import type { PeachLicenseExport, VaultEntry } from "../shared/types";19import type { VaultSettings } from "../background/vault";20212223242526export interface VaultBackupPayload {27 v: 3;28 entries: VaultEntry[];29 settings: VaultSettings;30 license?: PeachLicenseExport;31}3233export type { PeachLicenseExport };34353637383940export function serialiseVault(41 entries: VaultEntry[],42 settings: VaultSettings,43 license?: PeachLicenseExport44): Uint8Array {45 const payload: VaultBackupPayload = {46 v: 3,47 entries: entries.map(stripEphemeral),48 settings: { ...settings },49 ...(license ? { license } : {}),50 };51 return new TextEncoder().encode(JSON.stringify(payload));52}53545556575859export function deserialiseVault(bytes: Uint8Array): VaultBackupPayload {60 let parsed: unknown;61 try {62 parsed = JSON.parse(new TextDecoder().decode(bytes));63 } catch {64 throw new Error("DESERIALISE_MALFORMED");65 }6667 if (!parsed || typeof parsed !== "object") {68 throw new Error("DESERIALISE_MALFORMED");69 }7071 const candidate = parsed as Partial<VaultBackupPayload>;72 if (candidate.v !== 3 || !Array.isArray(candidate.entries)) {73 throw new Error("DESERIALISE_MALFORMED");74 }7576 const entries = candidate.entries.filter((e): e is VaultEntry =>77 Boolean(e && typeof e === "object" && typeof (e as VaultEntry).id === "string")78 );7980 const settings =81 candidate.settings && typeof candidate.settings === "object"82 ? (candidate.settings as VaultSettings)83 : {};8485 const license =86 candidate.license &&87 typeof candidate.license === "object" &&88 typeof (candidate.license as PeachLicenseExport).key === "string"89 ? (candidate.license as PeachLicenseExport)90 : undefined;9192 return { v: 3, entries, settings, ...(license ? { license } : {}) };93}949596979899100101102function stripEphemeral(entry: VaultEntry): VaultEntry {103 return entry;104}