fix(canvas): sanitize snapshot session.currentPageId before loadSnapshot — stale snapshots with missing pages caused currentPageId crash
Some checks failed
app-ci-deploy / test-build-deploy (push) Has been cancelled

- Pre-load: if session.currentPageId not in snapshot pages, reset to first available page
- Post-load: if store instance.currentPageId still invalid, clear store for fresh start
- On loadSnapshot error: clear store instead of silently failing with corrupt state
- Root cause: teacher1 had a saved snapshot with currentPageId pointing to a deleted page
This commit is contained in:
CC Worker 2026-06-01 06:02:19 +00:00
parent a375fbe37f
commit 9f58917c39

View File

@ -1,5 +1,5 @@
// External imports
import { TLStore, getSnapshot, Editor, loadSnapshot } from '@tldraw/tldraw';
import { TLStore, getSnapshot, Editor, loadSnapshot, TLINSTANCE_ID } from '@tldraw/tldraw';
import logger from '../../debugConfig';
import { SharedStoreService } from './sharedStoreService';
@ -109,17 +109,35 @@ export class NavigationSnapshotService {
return;
}
// Sanitize session.currentPageId against pages actually in the document
const docStore = (snap.document as { store?: Record<string, { typeName: string; id: string }> })?.store ?? {};
const pageIds = Object.values(docStore).filter((r) => (r as { typeName: string }).typeName === 'page').map((r) => r.id);
const session = snap.session as { currentPageId?: string } | undefined;
if (session?.currentPageId && pageIds.length > 0 && !pageIds.includes(session.currentPageId)) {
logger.warn('snapshot-service', '⚠️ session.currentPageId not in snapshot pages — resetting to first page', {
currentPageId: session.currentPageId, availablePages: pageIds
});
session.currentPageId = pageIds[0];
}
const snapshotCopy = {
schemaVersion: snap.schemaVersion || (snap.document as { schema?: { schemaVersion?: unknown } })?.schema?.schemaVersion,
document: snap.document,
session: snap.session,
};
const targetStore = editor ? editor.store : store;
try {
if (editor) {
loadSnapshot(editor.store, snapshotCopy as any);
} else {
loadSnapshot(store, snapshotCopy as any);
loadSnapshot(targetStore, snapshotCopy as any);
// Post-load validation: ensure instance.currentPageId is still valid
const instance = targetStore.get(TLINSTANCE_ID);
if (instance) {
const pages = targetStore.allRecords().filter((r) => (r as { typeName: string }).typeName === 'page');
const pageValid = pages.some((p) => p.id === instance.currentPageId);
if (!pageValid) {
logger.warn('snapshot-service', '⚠️ Post-load: currentPageId invalid — clearing corrupt store', { currentPageId: instance.currentPageId });
targetStore.clear();
}
}
logger.debug('snapshot-service', '✅ Snapshot loaded successfully');
} catch (err) {
@ -128,7 +146,8 @@ export class NavigationSnapshotService {
if (isSchemaMigration) {
logger.debug('snapshot-service', ' Schema migration warning (non-critical)', { error: msg });
} else {
logger.warn('snapshot-service', '⚠️ Unexpected loadSnapshot error', { error: msg });
logger.warn('snapshot-service', '⚠️ Unexpected loadSnapshot error — clearing store to allow fresh start', { error: msg });
targetStore.clear();
}
}