c9a5d79ae31e
fix: add getVaultVersion, addEntryDirect, updateEntryDirect, deleteEntryDirect to vault.ts (v3.6.98)
- time
- 2026-05-22 18:31 UTC · 5 months ago
- machine
- koh2
- parent
- 5fa72ee8d048
↓ peach-browser-c9a5d79a.tar.gzatlas at this save
files changed2 files
src/background/chirp-sync-vault-adapter.ts+2 −12src/background/vault.ts+52 −0src/background/chirp-sync-vault-adapter.ts+2 −12
@@ -36,6 +36,7 @@3636 getDerivedKeyHex,3737 getEntries,3838 getEntryById,39+ getVaultVersion,3940 isUnlocked,4041 } from "./vault";4142 import {@@ -74,18 +75,7 @@7475 },7576 7677 async getLocalVersion() {77- try {78- const raw = await chrome.storage.local.get("peach_last_modified_at");79- const v = (raw as Record<string, unknown>).peach_last_modified_at;80- if (typeof v === "number") return v;81- if (typeof v === "string" && v !== "") {82- const n = Number.parseInt(v, 10);83- return Number.isFinite(n) ? n : 0;84- }85- return 0;86- } catch {87- return 0;88- }78+ return getVaultVersion();8979 },9080 9181 async buildEntryManifest() {src/background/vault.ts+52 −0
@@ -1672,6 +1672,58 @@16721672 }16731673 16741674 /**1675+ * Returns the vault's monotonic version — the `peach_last_modified_at` value1676+ * stamped on every user-initiated write. Remote (sync) writes do NOT advance1677+ * this version so a sync round never cascades into a second outbound sync.1678+ */1679+export async function getVaultVersion(): Promise<number> {1680+ try {1681+ const raw = await chrome.storage.local.get("peach_last_modified_at");1682+ const v = (raw as Record<string, unknown>).peach_last_modified_at;1683+ if (typeof v === "number") return v;1684+ if (typeof v === "string" && v !== "") {1685+ const n = Number.parseInt(v, 10);1686+ return Number.isFinite(n) ? n : 0;1687+ }1688+ return 0;1689+ } catch {1690+ return 0;1691+ }1692+}1693+1694+/**1695+ * Internal write variants — bypass `fireSyncTrigger` so the sync layer can1696+ * apply entries without cascading into another outbound sync round.1697+ *1698+ * INVARIANT: callers must not pass entries whose IDs collide with existing1699+ * active (non-deleted) entries unless the intent is an explicit overwrite.1700+ * These functions do not validate field-level constraints (password required,1701+ * card number required, etc.) — they are trust-the-caller paths for sync and1702+ * test harness use only.1703+ */1704+export async function addEntryDirect(entry: VaultEntry): Promise<void> {1705+ if (!derivedKey) throw new Error("vault_locked");1706+ entries = [...entries, entry];1707+ await persistEntries("sync_received");1708+}1709+1710+export async function updateEntryDirect(patch: VaultEntry): Promise<void> {1711+ if (!derivedKey) throw new Error("vault_locked");1712+ const idx = entries.findIndex((e) => e.id === patch.id);1713+ if (idx === -1) throw new Error("entry_not_found");1714+ entries[idx] = { ...entries[idx], ...patch };1715+ await persistEntries("sync_received");1716+}1717+1718+export async function deleteEntryDirect(id: string, deletedAt: number): Promise<void> {1719+ if (!derivedKey) throw new Error("vault_locked");1720+ const idx = entries.findIndex((e) => e.id === id);1721+ if (idx === -1) throw new Error("entry_not_found");1722+ entries[idx] = { ...entries[idx], deletedAt, modifiedAt: deletedAt };1723+ await persistEntries("sync_received");1724+}1725+1726+/**16751727 * Apply a batch of remote entries + tombstones from a chirp-sync peer.16761728 * Last-write-wins by `modifiedAt`; a tombstone supersedes a live entry when16771729 * `deletedAt > modifiedAt` on the local side. Mutates `entries` directly and