mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 23:21:08 +02:00
chore(boot): sweep orphan migration flags from localStorage
Two one-shot bootstraps left a per-user flag in localStorage so they
wouldn't run twice — and after F7 deleted the helpers themselves
(2a8e8ff98), the flags pointed at code that no longer existed:
mana.profile.silentTwinRepair.<userId>
mana.profile.avatarMigration.<userId>
New \`cleanupOrphanMigrationFlags()\` runs once per page load from the
(app) layout's onMount, right after \`restoreClientIdFromDexie()\`.
Cheap (single localStorage scan), idempotent (no-op once swept),
silent on private-mode / quota errors. The known-orphan prefix list
lives in the helper file with deletion-commit refs so it's clear
when each entry can be retired.
Future migration deletions: append the prefix to ORPHAN_KEY_PREFIXES
in the same commit that drops the helper, and the next page load
on every device cleans up.
Closes Punkt 8 of the F1-F7 follow-up audit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
cabfd1004d
commit
119cd2cf83
2 changed files with 70 additions and 0 deletions
66
apps/mana/apps/web/src/lib/data/migrations-cleanup.ts
Normal file
66
apps/mana/apps/web/src/lib/data/migrations-cleanup.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* One-shot cleanup of orphan localStorage keys left behind by deleted
|
||||
* migration helpers.
|
||||
*
|
||||
* When a one-shot bootstrap (e.g. `repairSilentTwinAvatarRows`) finishes
|
||||
* the first time, it sets a per-user `mana.profile.<name>.<uid>` flag in
|
||||
* localStorage so it doesn't run again. After the helper itself is
|
||||
* deleted (F7 of docs/plans/sync-field-meta-overhaul.md), the flag stays
|
||||
* behind forever — a small chunk of dead state that piles up if more
|
||||
* helpers come and go.
|
||||
*
|
||||
* This module sweeps the known orphan prefixes and removes any matching
|
||||
* key on every page load. Cheap (a single localStorage scan, capped by
|
||||
* the browser's quota) and idempotent — calling it twice is a no-op
|
||||
* the second time because the keys are already gone.
|
||||
*
|
||||
* Add a new prefix here whenever a migration helper is deleted in a
|
||||
* future commit, then remove the prefix again once enough time has
|
||||
* passed that no live device still carries the orphan flag.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Known dead localStorage prefixes. Keep in chronological order with a
|
||||
* comment naming the deletion commit so it's clear when the entry can
|
||||
* be removed once enough time has passed.
|
||||
*/
|
||||
const ORPHAN_KEY_PREFIXES: readonly string[] = [
|
||||
// F7: `repair-silent-twin.ts` deleted in 2a8e8ff98 (sync field-meta
|
||||
// overhaul). The helper guarded itself with this flag so it'd only
|
||||
// run once per user; the flag now points at code that no longer
|
||||
// exists.
|
||||
'mana.profile.silentTwinRepair.',
|
||||
// F7: `legacy-avatar.ts` deleted in the same commit, same flag
|
||||
// pattern, same orphan situation.
|
||||
'mana.profile.avatarMigration.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Remove every localStorage key matching one of {@link ORPHAN_KEY_PREFIXES}.
|
||||
*
|
||||
* Best-effort: failures (private mode, quota errors, locked storage on
|
||||
* some browsers) are swallowed silently — orphan flags are cosmetic, not
|
||||
* load-bearing.
|
||||
*/
|
||||
export function cleanupOrphanMigrationFlags(): void {
|
||||
if (typeof localStorage === 'undefined') return;
|
||||
const toRemove: string[] = [];
|
||||
try {
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if (!key) continue;
|
||||
if (ORPHAN_KEY_PREFIXES.some((prefix) => key.startsWith(prefix))) {
|
||||
toRemove.push(key);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
for (const key of toRemove) {
|
||||
try {
|
||||
localStorage.removeItem(key);
|
||||
} catch {
|
||||
// Storage locked / private mode — next reload retries.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -70,6 +70,7 @@
|
|||
stopMemoroLlmWatcher,
|
||||
} from '$lib/modules/memoro/llm-watcher.svelte';
|
||||
import { createUnifiedSync, restoreClientIdFromDexie } from '$lib/data/sync';
|
||||
import { cleanupOrphanMigrationFlags } from '$lib/data/migrations-cleanup';
|
||||
import { syncBilling } from '$lib/stores/sync-billing.svelte';
|
||||
import { networkStore } from '$lib/stores/network.svelte';
|
||||
import { db } from '$lib/data/database';
|
||||
|
|
@ -632,6 +633,9 @@
|
|||
// so the next push/pull keeps the same identity the server
|
||||
// already knows.
|
||||
await restoreClientIdFromDexie();
|
||||
// Sweep stale localStorage flags from migration helpers that
|
||||
// have since been deleted (F7 + future cleanups).
|
||||
cleanupOrphanMigrationFlags();
|
||||
const getToken = () => authStore.getValidToken();
|
||||
unifiedSync = createUnifiedSync(SYNC_SERVER_URL, getToken, syncBilling.active);
|
||||
// Expose on window for SYNC_DEBUG.md (Schritt C). Not a security
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue