diff options
Diffstat (limited to 'apps/web/src/services/storage/migrations')
4 files changed, 113 insertions, 1 deletions
diff --git a/apps/web/src/services/storage/migrations/__tests__/v31-to-v32.test.ts b/apps/web/src/services/storage/migrations/__tests__/v31-to-v32.test.ts new file mode 100644 index 00000000..e3f1af8c --- /dev/null +++ b/apps/web/src/services/storage/migrations/__tests__/v31-to-v32.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, test } from "bun:test"; +import { transformProjectV31ToV32 } from "../transformers/v31-to-v32"; +import { asRecord, asRecordArray } from "./helpers"; + +describe("V31 to V32 Migration", () => { + test("stamps a v31 project as v32 without changing its content", () => { + const result = transformProjectV31ToV32({ + project: { + id: "project-v31", + version: 31, + metadata: { id: "project-v31", name: "My project" }, + scenes: [ + { + id: "scene-1", + tracks: { + main: { + id: "track-1", + type: "video", + elements: [{ id: "elem-1", type: "video", mediaId: "media-1" }], + }, + overlay: [], + audio: [], + }, + }, + ], + }, + }); + + expect(result.skipped).toBe(false); + expect(result.project.version).toBe(32); + const scene = asRecordArray(result.project.scenes)[0]; + const main = asRecord(asRecord(scene.tracks).main); + const element = asRecordArray(main.elements)[0]; + expect(element).toMatchObject({ + id: "elem-1", + type: "video", + mediaId: "media-1", + }); + expect(asRecord(result.project.metadata).name).toBe("My project"); + }); + + test("skips a project that is already v32", () => { + const project = { id: "p1", version: 32, scenes: [] }; + const result = transformProjectV31ToV32({ project }); + expect(result.skipped).toBe(true); + expect(result.reason).toBe("already v32"); + expect(result.project).toBe(project); + }); + + test("skips a project that is not v31", () => { + const project = { id: "p1", version: 30, scenes: [] }; + const result = transformProjectV31ToV32({ project }); + expect(result.skipped).toBe(true); + expect(result.reason).toBe("not v31"); + expect(result.project).toBe(project); + }); + + test("skips a project without an id", () => { + const project = { version: 31, scenes: [] }; + const result = transformProjectV31ToV32({ project }); + expect(result.skipped).toBe(true); + expect(result.reason).toBe("no project id"); + expect(result.project).toBe(project); + }); +}); diff --git a/apps/web/src/services/storage/migrations/index.ts b/apps/web/src/services/storage/migrations/index.ts index cbc35bb3..f3f2a22c 100644 --- a/apps/web/src/services/storage/migrations/index.ts +++ b/apps/web/src/services/storage/migrations/index.ts @@ -30,10 +30,11 @@ import { V27toV28Migration } from "./v27-to-v28"; import { V28toV29Migration } from "./v28-to-v29"; import { V29toV30Migration } from "./v29-to-v30"; import { V30toV31Migration } from "./v30-to-v31"; +import { V31toV32Migration } from "./v31-to-v32"; export { runStorageMigrations } from "./runner"; export type { MigrationProgress } from "./runner"; -export const CURRENT_PROJECT_VERSION = 31; +export const CURRENT_PROJECT_VERSION = 32; export const migrations = [ new V0toV1Migration(), @@ -67,4 +68,5 @@ export const migrations = [ new V28toV29Migration(), new V29toV30Migration(), new V30toV31Migration(), + new V31toV32Migration(), ]; diff --git a/apps/web/src/services/storage/migrations/transformers/v31-to-v32.ts b/apps/web/src/services/storage/migrations/transformers/v31-to-v32.ts new file mode 100644 index 00000000..bedb4784 --- /dev/null +++ b/apps/web/src/services/storage/migrations/transformers/v31-to-v32.ts @@ -0,0 +1,31 @@ +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId } from "./utils"; + +/** + * v31 → v32 introduces the `projectVersions` store (named checkpoints + + * auto-checkpoints; see notes/project-versioning.md). The store lives in its + * own IndexedDB database created lazily by the versions adapter, so project + * records need no shape change — this migration only stamps the new version. + */ +export function transformProjectV31ToV32({ + project, +}: { + project: ProjectRecord; +}): MigrationResult<ProjectRecord> { + if (!getProjectId({ project })) { + return { project, skipped: true, reason: "no project id" }; + } + + const version = project.version; + if (typeof version !== "number") { + return { project, skipped: true, reason: "invalid version" }; + } + if (version >= 32) { + return { project, skipped: true, reason: "already v32" }; + } + if (version !== 31) { + return { project, skipped: true, reason: "not v31" }; + } + + return { project: { ...project, version: 32 }, skipped: false }; +} diff --git a/apps/web/src/services/storage/migrations/v31-to-v32.ts b/apps/web/src/services/storage/migrations/v31-to-v32.ts new file mode 100644 index 00000000..0b6da324 --- /dev/null +++ b/apps/web/src/services/storage/migrations/v31-to-v32.ts @@ -0,0 +1,14 @@ +import { StorageMigration, type StorageMigrationRunArgs } from "./base"; +import type { MigrationResult, ProjectRecord } from "./transformers/types"; +import { transformProjectV31ToV32 } from "./transformers/v31-to-v32"; + +export class V31toV32Migration extends StorageMigration { + from = 31; + to = 32; + + async run({ + project, + }: StorageMigrationRunArgs): Promise<MigrationResult<ProjectRecord>> { + return transformProjectV31ToV32({ project }); + } +} |
