From 0e4ae079ac01a82115f499e73a8e75bd2cd974d0 Mon Sep 17 00:00:00 2001 From: Ryan Di Date: Mon, 23 Mar 2026 17:30:11 +1100 Subject: [PATCH] refactor: remove payload-level schema version --- excalidraw-app/App.tsx | 63 +++---------------- excalidraw-app/app_constants.ts | 1 - excalidraw-app/data/LocalData.ts | 12 +--- excalidraw-app/data/index.ts | 2 - excalidraw-app/data/localStorage.ts | 15 +---- packages/excalidraw/clipboard.ts | 15 +---- packages/excalidraw/components/App.tsx | 53 ++-------------- .../excalidraw/components/PublishLibrary.tsx | 3 - .../excalidraw/components/TTDDialog/common.ts | 3 - packages/excalidraw/data/blob.ts | 12 +--- packages/excalidraw/data/json.ts | 3 - packages/excalidraw/data/library.ts | 24 +------ 12 files changed, 23 insertions(+), 183 deletions(-) diff --git a/excalidraw-app/App.tsx b/excalidraw-app/App.tsx index e3691c6d72..74655b387a 100644 --- a/excalidraw-app/App.tsx +++ b/excalidraw-app/App.tsx @@ -53,11 +53,6 @@ import { restoreAppState, restoreElements, } from "@excalidraw/excalidraw/data/restore"; -import { - hasElementSchemaVersion, - migrateSceneElements, - SCHEMA_VERSIONS, -} from "@excalidraw/excalidraw/data/schema"; import { newElementWith } from "@excalidraw/element"; import { isInitializedImageElement } from "@excalidraw/element"; import clsx from "clsx"; @@ -232,17 +227,6 @@ const initializeScene = async (opts: { const externalUrlMatch = window.location.hash.match(/^#url=(.*)$/); const localDataState = importFromLocalStorage(); - const localStorageSchemaVersionSource = hasElementSchemaVersion( - localDataState?.elements, - ) - ? { - payloadSchemaVersion: undefined, - fallbackVersion: SCHEMA_VERSIONS.initial, - } - : { - payloadSchemaVersion: localDataState?.schemaVersion, - fallbackVersion: SCHEMA_VERSIONS.initial, - }; let scene: Omit< RestoredDataState, @@ -252,17 +236,10 @@ const initializeScene = async (opts: { > & { scrollToContent?: boolean; } = { - elements: restoreElements( - migrateSceneElements( - localDataState?.elements, - localStorageSchemaVersionSource, - ), - null, - { - repairBindings: true, - deleteInvisibleElements: true, - }, - ), + elements: restoreElements(localDataState?.elements, null, { + repairBindings: true, + deleteInvisibleElements: true, + }), appState: restoreAppState(localDataState?.appState, null), }; @@ -285,17 +262,10 @@ const initializeScene = async (opts: { scene = { elements: bumpElementVersions( - restoreElements( - migrateSceneElements(imported.elements, { - payloadSchemaVersion: imported.schemaVersion, - fallbackVersion: SCHEMA_VERSIONS.initial, - }), - null, - { - repairBindings: true, - deleteInvisibleElements: true, - }, - ), + restoreElements(imported.elements, null, { + repairBindings: true, + deleteInvisibleElements: true, + }), scene.elements, ), appState: restoreAppState( @@ -579,24 +549,9 @@ const ExcalidrawWrapper = () => { if (isBrowserStorageStateNewer(STORAGE_KEYS.VERSION_DATA_STATE)) { const localDataState = importFromLocalStorage(); const username = importUsernameFromLocalStorage(); - const localStorageSchemaVersionSource = hasElementSchemaVersion( - localDataState?.elements, - ) - ? { - payloadSchemaVersion: undefined, - fallbackVersion: SCHEMA_VERSIONS.initial, - } - : { - payloadSchemaVersion: localDataState?.schemaVersion, - fallbackVersion: SCHEMA_VERSIONS.initial, - }; - const migratedElements = migrateSceneElements( - localDataState?.elements, - localStorageSchemaVersionSource, - ); setLangCode(getPreferredLanguage()); excalidrawAPI.updateScene({ - elements: restoreElements(migratedElements, null, { + elements: restoreElements(localDataState?.elements, null, { repairBindings: true, deleteInvisibleElements: true, }), diff --git a/excalidraw-app/app_constants.ts b/excalidraw-app/app_constants.ts index 5842360360..e4370e7986 100644 --- a/excalidraw-app/app_constants.ts +++ b/excalidraw-app/app_constants.ts @@ -39,7 +39,6 @@ export const ROOM_ID_BYTES = 10; export const STORAGE_KEYS = { LOCAL_STORAGE_ELEMENTS: "excalidraw", LOCAL_STORAGE_APP_STATE: "excalidraw-state", - LOCAL_STORAGE_SCHEMA_VERSION: "excalidraw-schema-version", LOCAL_STORAGE_COLLAB: "excalidraw-collab", LOCAL_STORAGE_THEME: "excalidraw-theme", LOCAL_STORAGE_DEBUG: "excalidraw-debug", diff --git a/excalidraw-app/data/LocalData.ts b/excalidraw-app/data/LocalData.ts index c81d6b092d..fb3ec36f74 100644 --- a/excalidraw-app/data/LocalData.ts +++ b/excalidraw-app/data/LocalData.ts @@ -28,7 +28,6 @@ import { import { appJotaiStore, atom } from "excalidraw-app/app-jotai"; import { getNonDeletedElements } from "@excalidraw/element"; -import { SCHEMA_VERSIONS } from "@excalidraw/excalidraw/data/schema"; import type { LibraryPersistedData } from "@excalidraw/excalidraw/data/library"; import type { ImportedDataState } from "@excalidraw/excalidraw/data/types"; @@ -87,12 +86,7 @@ const saveDataStateToLocalStorage = ( _appState.openSidebar = null; } - const persistedElements = getNonDeletedElements(elements).map( - (element) => ({ - ...element, - schemaVersion: SCHEMA_VERSIONS.latest, - }), - ); + const persistedElements = getNonDeletedElements(elements); localStorage.setItem( STORAGE_KEYS.LOCAL_STORAGE_ELEMENTS, @@ -102,10 +96,6 @@ const saveDataStateToLocalStorage = ( STORAGE_KEYS.LOCAL_STORAGE_APP_STATE, JSON.stringify(_appState), ); - localStorage.setItem( - STORAGE_KEYS.LOCAL_STORAGE_SCHEMA_VERSION, - `${SCHEMA_VERSIONS.latest}`, - ); updateBrowserStateVersion(STORAGE_KEYS.VERSION_DATA_STATE); if (localStorageQuotaExceeded) { appJotaiStore.set(localStorageQuotaExceededAtom, false); diff --git a/excalidraw-app/data/index.ts b/excalidraw-app/data/index.ts index 1afcb020a3..bf765941a2 100644 --- a/excalidraw-app/data/index.ts +++ b/excalidraw-app/data/index.ts @@ -196,7 +196,6 @@ const legacy_decodeFromBackend = async ({ return { elements: data.elements || null, appState: data.appState || null, - schemaVersion: data.schemaVersion, }; }; @@ -227,7 +226,6 @@ export const importFromBackend = async ( return { elements: data.elements || null, appState: data.appState || null, - schemaVersion: data.schemaVersion, }; } catch (error: any) { console.warn( diff --git a/excalidraw-app/data/localStorage.ts b/excalidraw-app/data/localStorage.ts index 4f6036a36a..28c166cd74 100644 --- a/excalidraw-app/data/localStorage.ts +++ b/excalidraw-app/data/localStorage.ts @@ -2,10 +2,6 @@ import { clearAppStateForLocalStorage, getDefaultAppState, } from "@excalidraw/excalidraw/appState"; -import { - resolveSchemaVersion, - SCHEMA_VERSIONS, -} from "@excalidraw/excalidraw/data/schema"; import type { ExcalidrawElement } from "@excalidraw/element/types"; import type { AppState } from "@excalidraw/excalidraw/types"; @@ -41,14 +37,10 @@ export const importUsernameFromLocalStorage = (): string | null => { export const importFromLocalStorage = () => { let savedElements = null; let savedState = null; - let savedSchemaVersion = null; try { savedElements = localStorage.getItem(STORAGE_KEYS.LOCAL_STORAGE_ELEMENTS); savedState = localStorage.getItem(STORAGE_KEYS.LOCAL_STORAGE_APP_STATE); - savedSchemaVersion = localStorage.getItem( - STORAGE_KEYS.LOCAL_STORAGE_SCHEMA_VERSION, - ); } catch (error: any) { // Unable to access localStorage console.error(error); @@ -78,12 +70,7 @@ export const importFromLocalStorage = () => { // Do nothing because appState is already null } } - const schemaVersion = resolveSchemaVersion( - Number(savedSchemaVersion), - SCHEMA_VERSIONS.initial, - ); - - return { elements, appState, schemaVersion }; + return { elements, appState }; }; export const getElementsStorageSize = () => { diff --git a/packages/excalidraw/clipboard.ts b/packages/excalidraw/clipboard.ts index 07bbfb39e0..2cbca45cba 100644 --- a/packages/excalidraw/clipboard.ts +++ b/packages/excalidraw/clipboard.ts @@ -35,8 +35,6 @@ import { import { tryParseSpreadsheet, VALID_SPREADSHEET } from "./charts"; -import { SCHEMA_VERSIONS } from "./data/schema"; - import type { FileSystemHandle } from "./data/filesystem"; import type { Spreadsheet } from "./charts"; @@ -45,7 +43,6 @@ import type { BinaryFiles } from "./types"; type ElementsClipboard = { type: typeof EXPORT_DATA_TYPES.excalidrawClipboard; - schemaVersion: number; elements: readonly (NonDeletedExcalidrawElement & { schemaVersion?: number; })[]; @@ -58,7 +55,6 @@ export interface ClipboardData { spreadsheet?: Spreadsheet; elements?: readonly ExcalidrawElement[]; files?: BinaryFiles; - schemaVersion?: number; text?: string; mixedContent?: PastedMixedContent; errorMessage?: string; @@ -88,7 +84,6 @@ const clipboardContainsElements = ( ): contents is { elements: ExcalidrawElement[]; files?: BinaryFiles; - schemaVersion?: number; } => { if ( [ @@ -188,24 +183,19 @@ export const serializeAsClipboardJSON = ({ // select bound text elements when copying const contents: ElementsClipboard = { type: EXPORT_DATA_TYPES.excalidrawClipboard, - schemaVersion: SCHEMA_VERSIONS.latest, elements: elements.map((element) => { - const elementWithSchema = { - ...element, - schemaVersion: SCHEMA_VERSIONS.latest, - }; if ( getContainingFrame(element, elementsMap) && !framesToCopy.has(getContainingFrame(element, elementsMap)!) ) { - const copiedElement = deepCopyElement(elementWithSchema); + const copiedElement = deepCopyElement(element); mutateElement(copiedElement, elementsMap, { frameId: null, }); return copiedElement; } - return elementWithSchema; + return element; }), files: files ? _files : undefined, }; @@ -587,7 +577,6 @@ export const parseClipboard = async ( return { elements: systemClipboardData.elements, files: systemClipboardData.files, - schemaVersion: systemClipboardData.schemaVersion, text: isPlainPaste ? JSON.stringify(systemClipboardData.elements, null, 2) : undefined, diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index 1f8b4f41d7..d0ad28409f 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -33,7 +33,6 @@ import { ELEMENT_SHIFT_TRANSLATE_AMOUNT, ELEMENT_TRANSLATE_AMOUNT, EVENT, - EXPORT_DATA_TYPES, FRAME_STYLE, IMAGE_MIME_TYPES, IMAGE_RENDER_TIMEOUT, @@ -355,15 +354,6 @@ import { import { exportCanvas, loadFromBlob } from "../data"; import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library"; import { restoreAppState, restoreElements } from "../data/restore"; -import { - migrateAPIElements, - migrateClipboardElements, - migrateLibraryElements, - migrateSceneElements, - SCHEMA_VERSIONS, - type SchemaVersionSource, - type SchemaMigrationScope, -} from "../data/schema"; import { getCenter, getDistance } from "../gesture"; import { History } from "../history"; import { defaultLang, getLanguage, languages, setLanguage, t } from "../i18n"; @@ -2335,8 +2325,6 @@ class App extends React.Component { elements, position: "center", files: null, - schemaVersionSource: SCHEMA_VERSIONS.latest, - migrationScope: "api", }); }; @@ -2817,21 +2805,10 @@ class App extends React.Component { }, }; } - const initialDataSchemaFallback = - initialData?.type === EXPORT_DATA_TYPES.excalidraw - ? SCHEMA_VERSIONS.initial - : SCHEMA_VERSIONS.latest; - const restoredElements = restoreElements( - migrateSceneElements(initialData?.elements, { - payloadSchemaVersion: initialData?.schemaVersion, - fallbackVersion: initialDataSchemaFallback, - }), - null, - { - repairBindings: true, - deleteInvisibleElements: true, - }, - ); + const restoredElements = restoreElements(initialData?.elements, null, { + repairBindings: true, + deleteInvisibleElements: true, + }); let restoredAppState = restoreAppState(initialData?.appState, null); const activeTool = restoredAppState.activeTool; @@ -3596,13 +3573,6 @@ class App extends React.Component { this.addElementsFromPasteOrLibrary({ elements, files: data.files || null, - schemaVersionSource: data.programmaticAPI - ? SCHEMA_VERSIONS.latest - : { - payloadSchemaVersion: data.schemaVersion, - fallbackVersion: SCHEMA_VERSIONS.initial, - }, - migrationScope: data.programmaticAPI ? "api" : "clipboard", position: this.editorInterface.formFactor === "desktop" ? "cursor" : "center", retainSeed: isPlainPaste, @@ -3629,8 +3599,6 @@ class App extends React.Component { this.addElementsFromPasteOrLibrary({ elements, files, - schemaVersionSource: SCHEMA_VERSIONS.latest, - migrationScope: "api", position: this.editorInterface.formFactor === "desktop" ? "cursor" : "center", }); @@ -3748,20 +3716,11 @@ class App extends React.Component { addElementsFromPasteOrLibrary = (opts: { elements: readonly ExcalidrawElement[]; files: BinaryFiles | null; - schemaVersionSource: SchemaVersionSource; - migrationScope: SchemaMigrationScope; position: { clientX: number; clientY: number } | "cursor" | "center"; retainSeed?: boolean; fitToContent?: boolean; }) => { - const migratedElements = - opts.migrationScope === "clipboard" - ? migrateClipboardElements(opts.elements, opts.schemaVersionSource) - : opts.migrationScope === "library" - ? migrateLibraryElements(opts.elements, opts.schemaVersionSource) - : migrateAPIElements(opts.elements, opts.schemaVersionSource); - - const elements = restoreElements(migratedElements, null, { + const elements = restoreElements(opts.elements, null, { deleteInvisibleElements: true, }); const [minX, minY, maxX, maxY] = getCommonBounds(elements); @@ -11532,8 +11491,6 @@ class App extends React.Component { elements: distributeLibraryItemsOnSquareGrid(libraryItems), position: event, files: null, - schemaVersionSource: SCHEMA_VERSIONS.latest, - migrationScope: "library", }); } } catch (error: any) { diff --git a/packages/excalidraw/components/PublishLibrary.tsx b/packages/excalidraw/components/PublishLibrary.tsx index 23d711d2e5..0376f812a5 100644 --- a/packages/excalidraw/components/PublishLibrary.tsx +++ b/packages/excalidraw/components/PublishLibrary.tsx @@ -10,8 +10,6 @@ import { getExportSource, } from "@excalidraw/common"; -import { SCHEMA_VERSIONS } from "../data/schema"; - import { EditorLocalStorage } from "../data/EditorLocalStorage"; import { canvasToBlob, resizeImageFile } from "../data/blob"; import { t } from "../i18n"; @@ -282,7 +280,6 @@ const PublishLibrary = ({ const libContent: ExportedLibraryData = { type: EXPORT_DATA_TYPES.excalidrawLibrary, version: VERSIONS.excalidrawLibrary, - schemaVersion: SCHEMA_VERSIONS.latest, source: getExportSource(), libraryItems: clonedLibItems, }; diff --git a/packages/excalidraw/components/TTDDialog/common.ts b/packages/excalidraw/components/TTDDialog/common.ts index 2db863120b..f00f409132 100644 --- a/packages/excalidraw/components/TTDDialog/common.ts +++ b/packages/excalidraw/components/TTDDialog/common.ts @@ -6,7 +6,6 @@ import type { } from "@excalidraw/element/types"; import { EditorLocalStorage } from "../../data/EditorLocalStorage"; -import { SCHEMA_VERSIONS } from "../../data/schema"; import { convertToExcalidrawElements, exportToCanvas, @@ -149,8 +148,6 @@ export const insertToEditor = ({ app.addElementsFromPasteOrLibrary({ elements: newElements, files, - schemaVersionSource: SCHEMA_VERSIONS.latest, - migrationScope: "api", position: "center", fitToContent: true, }); diff --git a/packages/excalidraw/data/blob.ts b/packages/excalidraw/data/blob.ts index 978da1680a..c520c33b1c 100644 --- a/packages/excalidraw/data/blob.ts +++ b/packages/excalidraw/data/blob.ts @@ -24,7 +24,6 @@ import { restoreElements, restoreLibraryItems, } from "./restore"; -import { migrateSceneElements, SCHEMA_VERSIONS } from "./schema"; import type { AppState, DataURL, LibraryItem } from "../types"; @@ -158,14 +157,10 @@ export const loadSceneOrLibraryFromBlob = async ( throw error; } if (isValidExcalidrawData(data)) { - const migratedElements = migrateSceneElements(data.elements, { - payloadSchemaVersion: data.schemaVersion, - fallbackVersion: SCHEMA_VERSIONS.initial, - }); return { type: MIME_TYPES.excalidraw, data: { - elements: restoreElements(migratedElements, localElements, { + elements: restoreElements(data.elements, localElements, { repairBindings: true, deleteInvisibleElements: true, }), @@ -227,10 +222,7 @@ export const parseLibraryJSON = ( throw new Error("Invalid library"); } const libraryItems = data.libraryItems || data.library; - return restoreLibraryItems(libraryItems, defaultStatus, { - payloadSchemaVersion: data.schemaVersion, - fallbackVersion: SCHEMA_VERSIONS.initial, - }); + return restoreLibraryItems(libraryItems, defaultStatus); }; export const loadLibraryFromBlob = async ( diff --git a/packages/excalidraw/data/json.ts b/packages/excalidraw/data/json.ts index e2da81813b..047a2ccdec 100644 --- a/packages/excalidraw/data/json.ts +++ b/packages/excalidraw/data/json.ts @@ -12,7 +12,6 @@ import { cleanAppStateForExport, clearAppStateForDatabase } from "../appState"; import { isImageFileHandle, loadFromBlob } from "./blob"; import { fileOpen, fileSave } from "./filesystem"; -import { SCHEMA_VERSIONS } from "./schema"; import type { AppState, BinaryFiles, LibraryItems } from "../types"; import type { @@ -52,7 +51,6 @@ export const serializeAsJSON = ( const data: ExportedDataState = { type: EXPORT_DATA_TYPES.excalidraw, version: VERSIONS.excalidraw, - schemaVersion: SCHEMA_VERSIONS.latest, source: getExportSource(), elements, appState: @@ -131,7 +129,6 @@ export const serializeLibraryAsJSON = (libraryItems: LibraryItems) => { const data: ExportedLibraryData = { type: EXPORT_DATA_TYPES.excalidrawLibrary, version: VERSIONS.excalidrawLibrary, - schemaVersion: SCHEMA_VERSIONS.latest, source: getExportSource(), libraryItems, }; diff --git a/packages/excalidraw/data/library.ts b/packages/excalidraw/data/library.ts index fc05126e2c..478b4ac27b 100644 --- a/packages/excalidraw/data/library.ts +++ b/packages/excalidraw/data/library.ts @@ -33,7 +33,6 @@ import { t } from "../i18n"; import { loadLibraryFromBlob } from "./blob"; import { restoreLibraryItems } from "./restore"; -import { SCHEMA_VERSIONS } from "./schema"; import type App from "../components/App"; @@ -68,7 +67,6 @@ type LibraryUpdate = { export type LibraryPersistedData = { libraryItems: LibraryItems; - schemaVersion?: number; }; const onLibraryUpdateEmitter = new Emitter< @@ -91,10 +89,7 @@ export interface LibraryPersistenceAdapter { * purposes, in which case host app can implement more aggressive caching. */ source: LibraryAdatapterSource; - }): MaybePromise<{ - libraryItems: LibraryItems_anyVersion; - schemaVersion?: number; - } | null>; + }): MaybePromise<{ libraryItems: LibraryItems_anyVersion } | null>; /** Should persist to the database as is (do no change the data structure). */ save(libraryData: LibraryPersistedData): MaybePromise; } @@ -106,7 +101,6 @@ export interface LibraryMigrationAdapter { */ load(): MaybePromise<{ libraryItems: LibraryItems_anyVersion; - schemaVersion?: number; } | null>; /** clears entire storage afterwards */ @@ -562,12 +556,7 @@ class AdapterTransaction { new Promise(async (resolve, reject) => { try { const data = await adapter.load({ source }); - resolve( - restoreLibraryItems(data?.libraryItems || [], "published", { - payloadSchemaVersion: data?.schemaVersion, - fallbackVersion: SCHEMA_VERSIONS.initial, - }), - ); + resolve(restoreLibraryItems(data?.libraryItems || [], "published")); } catch (error: any) { reject(error); } @@ -675,10 +664,7 @@ const persistLibraryUpdate = async ( const version = getLibraryItemsHash(nextLibraryItems); if (version !== lastSavedLibraryItemsHash) { - await adapter.save({ - libraryItems: nextLibraryItems, - schemaVersion: SCHEMA_VERSIONS.latest, - }); + await adapter.save({ libraryItems: nextLibraryItems }); } lastSavedLibraryItemsHash = version; @@ -878,10 +864,6 @@ export const useHandleLibrary = ( restoredData = restoreLibraryItems( libraryData.libraryItems || [], "published", - { - payloadSchemaVersion: libraryData.schemaVersion, - fallbackVersion: SCHEMA_VERSIONS.initial, - }, ); // we don't queue this operation because it's running inside