From 4941cd075b5a71c5a1983e13ab4021c8cd90ff6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 14 Aug 2026 09:58:11 +0200 Subject: [PATCH] Carry the custom-metric entry tombstone through backup and restore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The entries gained a soft-delete this release; both backup ends follow in the same one. A portable export omits tombstoned readings so a restore cannot resurrect them; a disaster-recovery payload carries the tombstone and the restore writes it back, so the account returns exactly as it stood — undo affordance included. --- src/lib/export/profile-backup.ts | 12 +++++++++++- src/lib/validations/backup.ts | 3 +++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/lib/export/profile-backup.ts b/src/lib/export/profile-backup.ts index 50253e6b0..b0110f9a2 100644 --- a/src/lib/export/profile-backup.ts +++ b/src/lib/export/profile-backup.ts @@ -118,6 +118,8 @@ export interface CustomMetricEntryBackupEntry { measuredAt: string; note: string | null; createdAt?: string; + /** v1.37.20 (A3-11) — soft-delete tombstone; DR payloads only. */ + deletedAt?: string | null; } /** @@ -260,7 +262,13 @@ export async function buildProfileBackupSection( where: disasterRecovery ? { userId } : { userId, deletedAt: null }, orderBy: { name: "asc" }, include: { - entries: { orderBy: { measuredAt: "asc" } }, + // v1.37.20 (A3-11) — entries soft-delete now: a portable export + // omits tombstoned readings (a restore must not resurrect them); a + // DR payload carries them, tombstone included, like the parent. + entries: { + where: disasterRecovery ? {} : { deletedAt: null }, + orderBy: { measuredAt: "asc" }, + }, }, }), prisma.healthProfileFactRevision.findMany({ @@ -438,6 +446,7 @@ export async function buildProfileBackupSection( ? { id: entry.id, createdAt: entry.createdAt.toISOString(), + deletedAt: entry.deletedAt?.toISOString() ?? null, } : {}), value: entry.value, @@ -840,6 +849,7 @@ export async function restoreProfileData( unit: entry.unit, measuredAt: new Date(entry.measuredAt), note: entry.note, + deletedAt: entry.deletedAt ? new Date(entry.deletedAt) : null, ...(entry.createdAt ? { createdAt: new Date(entry.createdAt) } : {}), diff --git a/src/lib/validations/backup.ts b/src/lib/validations/backup.ts index 88c20d4c5..7b5c4c109 100644 --- a/src/lib/validations/backup.ts +++ b/src/lib/validations/backup.ts @@ -611,6 +611,9 @@ const customMetricEntryBackupSchema = z measuredAt: isoDateTime, note: z.string().nullable().default(null), createdAt: isoDateTime.optional(), + /// v1.37.20 (A3-11) — the entry tombstone rides DR payloads so a restore + /// brings the account back exactly as it stood, undo affordance included. + deletedAt: isoDateTime.nullable().optional(), }) .passthrough();