mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
codebase overhaul (#697)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
export abstract class StorageMigration {
|
||||
abstract from: number;
|
||||
abstract to: number;
|
||||
abstract run(): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export { StorageMigration } from "./base";
|
||||
export { StorageVersionManager } from "./version-manager";
|
||||
export { runStorageMigrations } from "./runner";
|
||||
import { V0toV1Migration } from "./v0-to-v1";
|
||||
import { V1toV2Migration } from "./v1-to-v2";
|
||||
import { V2toV3Migration } from "./v2-to-v3";
|
||||
|
||||
export const CURRENT_STORAGE_VERSION = 3;
|
||||
|
||||
export const migrations = [
|
||||
new V0toV1Migration(),
|
||||
new V1toV2Migration(),
|
||||
new V2toV3Migration(),
|
||||
];
|
||||
@@ -0,0 +1,134 @@
|
||||
import { IndexedDBAdapter } from "@/services/storage/indexeddb-adapter";
|
||||
import type { StorageMigration } from "./base";
|
||||
import { StorageVersionManager } from "./version-manager";
|
||||
|
||||
export interface StorageMigrationResult {
|
||||
fromVersion: number;
|
||||
toVersion: number;
|
||||
migrated: boolean;
|
||||
}
|
||||
|
||||
export type StorageMigrationCallbacks = {
|
||||
onMigrationStart?: ({
|
||||
fromVersion,
|
||||
toVersion,
|
||||
}: {
|
||||
fromVersion: number;
|
||||
toVersion: number;
|
||||
}) => void;
|
||||
onMigrationComplete?: ({
|
||||
fromVersion,
|
||||
toVersion,
|
||||
}: {
|
||||
fromVersion: number;
|
||||
toVersion: number;
|
||||
}) => void;
|
||||
};
|
||||
|
||||
type ProjectRecord = Record<string, unknown>;
|
||||
|
||||
export async function runStorageMigrations({
|
||||
migrations,
|
||||
versionManager = new StorageVersionManager(),
|
||||
callbacks,
|
||||
}: {
|
||||
migrations: StorageMigration[];
|
||||
versionManager?: StorageVersionManager;
|
||||
callbacks?: StorageMigrationCallbacks;
|
||||
}): Promise<StorageMigrationResult> {
|
||||
const versionRecord = await versionManager.getVersionRecord();
|
||||
const inferredVersion = versionRecord
|
||||
? null
|
||||
: await inferStorageVersionFromProjects();
|
||||
const fromVersion =
|
||||
versionRecord?.inProgress?.from ??
|
||||
versionRecord?.version ??
|
||||
inferredVersion ??
|
||||
0;
|
||||
|
||||
if (!versionRecord) {
|
||||
await versionManager.setVersion({ version: fromVersion });
|
||||
}
|
||||
|
||||
const orderedMigrations = [...migrations].sort((a, b) => a.from - b.from);
|
||||
let currentVersion = fromVersion;
|
||||
|
||||
for (const migration of orderedMigrations) {
|
||||
if (migration.from !== currentVersion) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await versionManager.setInProgress({
|
||||
from: migration.from,
|
||||
to: migration.to,
|
||||
});
|
||||
callbacks?.onMigrationStart?.({
|
||||
fromVersion: migration.from,
|
||||
toVersion: migration.to,
|
||||
});
|
||||
await migration.run();
|
||||
currentVersion = migration.to;
|
||||
await versionManager.setVersion({ version: currentVersion });
|
||||
await versionManager.clearInProgress();
|
||||
callbacks?.onMigrationComplete?.({
|
||||
fromVersion: migration.from,
|
||||
toVersion: migration.to,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
fromVersion,
|
||||
toVersion: currentVersion,
|
||||
migrated: currentVersion !== fromVersion,
|
||||
};
|
||||
}
|
||||
|
||||
async function inferStorageVersionFromProjects(): Promise<number> {
|
||||
const projectsAdapter = new IndexedDBAdapter<unknown>(
|
||||
"video-editor-projects",
|
||||
"projects",
|
||||
1,
|
||||
);
|
||||
const projects = await projectsAdapter.getAll();
|
||||
|
||||
if (projects.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let lowestVersion = Number.POSITIVE_INFINITY;
|
||||
|
||||
for (const project of projects) {
|
||||
const projectVersion = checkProjectVersion({ project });
|
||||
if (projectVersion < lowestVersion) {
|
||||
lowestVersion = projectVersion;
|
||||
}
|
||||
}
|
||||
|
||||
if (lowestVersion === Number.POSITIVE_INFINITY) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return lowestVersion;
|
||||
}
|
||||
|
||||
function checkProjectVersion({ project }: { project: unknown }): number {
|
||||
if (!isRecord(project)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const versionValue = project.version;
|
||||
if (typeof versionValue === "number") {
|
||||
return versionValue;
|
||||
}
|
||||
|
||||
const scenesValue = project.scenes;
|
||||
if (Array.isArray(scenesValue) && scenesValue.length > 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is ProjectRecord {
|
||||
return typeof value === "object" && value !== null;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import { IndexedDBAdapter } from "@/services/storage/indexeddb-adapter";
|
||||
import type { SerializedScene } from "@/services/storage/types";
|
||||
import { buildDefaultScene } from "@/lib/scenes";
|
||||
import type { TScene } from "@/types/timeline";
|
||||
import { StorageMigration } from "./base";
|
||||
|
||||
type ProjectRecord = Record<string, unknown>;
|
||||
|
||||
export class V0toV1Migration extends StorageMigration {
|
||||
from = 0;
|
||||
to = 1;
|
||||
|
||||
async run(): Promise<void> {
|
||||
const projectsAdapter = new IndexedDBAdapter<unknown>(
|
||||
"video-editor-projects",
|
||||
"projects",
|
||||
1,
|
||||
);
|
||||
const projects = await projectsAdapter.getAll();
|
||||
|
||||
for (const project of projects) {
|
||||
if (!isRecord(project)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const scenesValue = project.scenes;
|
||||
if (Array.isArray(scenesValue) && scenesValue.length > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mainScene = buildDefaultScene({ isMain: true, name: "Main scene" });
|
||||
const serializedScene = serializeScene({ scene: mainScene });
|
||||
const updatedProject: ProjectRecord = {
|
||||
...project,
|
||||
scenes: [serializedScene],
|
||||
currentSceneId: mainScene.id,
|
||||
version: 1,
|
||||
};
|
||||
|
||||
const updatedAt = new Date().toISOString();
|
||||
if (isRecord(project.metadata)) {
|
||||
updatedProject.metadata = {
|
||||
...project.metadata,
|
||||
updatedAt,
|
||||
};
|
||||
} else {
|
||||
updatedProject.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
const projectId = getProjectId({ project: updatedProject });
|
||||
if (!projectId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await projectsAdapter.set(projectId, updatedProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function serializeScene({ scene }: { scene: TScene }): SerializedScene {
|
||||
return {
|
||||
id: scene.id,
|
||||
name: scene.name,
|
||||
isMain: scene.isMain,
|
||||
tracks: scene.tracks,
|
||||
bookmarks: scene.bookmarks,
|
||||
createdAt: scene.createdAt.toISOString(),
|
||||
updatedAt: scene.updatedAt.toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
function getProjectId({ project }: { project: ProjectRecord }): string | null {
|
||||
const idValue = project.id;
|
||||
if (typeof idValue === "string" && idValue.length > 0) {
|
||||
return idValue;
|
||||
}
|
||||
|
||||
const metadataValue = project.metadata;
|
||||
if (!isRecord(metadataValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const metadataId = metadataValue.id;
|
||||
if (typeof metadataId === "string" && metadataId.length > 0) {
|
||||
return metadataId;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is ProjectRecord {
|
||||
return typeof value === "object" && value !== null;
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
import {
|
||||
DEFAULT_BLUR_INTENSITY,
|
||||
DEFAULT_CANVAS_SIZE,
|
||||
DEFAULT_COLOR,
|
||||
DEFAULT_FPS,
|
||||
} from "@/constants/project-constants";
|
||||
import { IndexedDBAdapter } from "@/services/storage/indexeddb-adapter";
|
||||
import { StorageMigration } from "./base";
|
||||
|
||||
type ProjectRecord = Record<string, unknown>;
|
||||
|
||||
export class V1toV2Migration extends StorageMigration {
|
||||
from = 1;
|
||||
to = 2;
|
||||
|
||||
async run(): Promise<void> {
|
||||
const projectsAdapter = new IndexedDBAdapter<unknown>(
|
||||
"video-editor-projects",
|
||||
"projects",
|
||||
1,
|
||||
);
|
||||
const projects = await projectsAdapter.getAll();
|
||||
|
||||
for (const project of projects) {
|
||||
if (!isRecord(project)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const projectId = getProjectId({ project });
|
||||
if (!projectId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isV2Project({ project })) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const migratedProject = migrateProject({ project, projectId });
|
||||
await projectsAdapter.set(projectId, migratedProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function migrateProject({
|
||||
project,
|
||||
projectId,
|
||||
}: {
|
||||
project: ProjectRecord;
|
||||
projectId: string;
|
||||
}): ProjectRecord {
|
||||
const createdAt = normalizeDateString({ value: project.createdAt });
|
||||
const updatedAt = normalizeDateString({ value: project.updatedAt });
|
||||
const metadataValue = project.metadata;
|
||||
|
||||
const metadata = isRecord(metadataValue)
|
||||
? {
|
||||
id: getStringValue({ value: metadataValue.id, fallback: projectId }),
|
||||
name: getStringValue({ value: metadataValue.name, fallback: "" }),
|
||||
thumbnail: getStringValue({ value: metadataValue.thumbnail }),
|
||||
createdAt: normalizeDateString({ value: metadataValue.createdAt }),
|
||||
updatedAt: normalizeDateString({ value: metadataValue.updatedAt }),
|
||||
}
|
||||
: {
|
||||
id: projectId,
|
||||
name: getStringValue({ value: project.name, fallback: "" }),
|
||||
thumbnail: getStringValue({ value: project.thumbnail }),
|
||||
createdAt,
|
||||
updatedAt,
|
||||
};
|
||||
|
||||
const scenesValue = project.scenes;
|
||||
const scenes = Array.isArray(scenesValue) ? scenesValue : [];
|
||||
const legacyBookmarks = Array.isArray(project.bookmarks)
|
||||
? project.bookmarks
|
||||
: null;
|
||||
const normalizedScenes = applyLegacyBookmarks({
|
||||
scenes,
|
||||
legacyBookmarks,
|
||||
});
|
||||
|
||||
const settingsValue = project.settings;
|
||||
const settings = isRecord(settingsValue)
|
||||
? {
|
||||
fps: getNumberValue({
|
||||
value: settingsValue.fps,
|
||||
fallback: DEFAULT_FPS,
|
||||
}),
|
||||
canvasSize: getCanvasSizeValue({
|
||||
value: settingsValue.canvasSize,
|
||||
fallback: DEFAULT_CANVAS_SIZE,
|
||||
}),
|
||||
background: getBackgroundValue({
|
||||
value: settingsValue.background,
|
||||
}),
|
||||
}
|
||||
: {
|
||||
fps: getNumberValue({ value: project.fps, fallback: DEFAULT_FPS }),
|
||||
canvasSize: getCanvasSizeValue({
|
||||
value: project.canvasSize,
|
||||
fallback: DEFAULT_CANVAS_SIZE,
|
||||
}),
|
||||
background: getBackgroundValue({
|
||||
value: project.background,
|
||||
backgroundType: project.backgroundType,
|
||||
backgroundColor: project.backgroundColor,
|
||||
blurIntensity: project.blurIntensity,
|
||||
}),
|
||||
};
|
||||
|
||||
const currentSceneId = getCurrentSceneId({
|
||||
value: project.currentSceneId,
|
||||
scenes: normalizedScenes,
|
||||
});
|
||||
|
||||
return {
|
||||
...project,
|
||||
metadata,
|
||||
scenes: normalizedScenes,
|
||||
currentSceneId,
|
||||
settings,
|
||||
version: 2,
|
||||
};
|
||||
}
|
||||
|
||||
function getProjectId({ project }: { project: ProjectRecord }): string | null {
|
||||
const idValue = project.id;
|
||||
if (typeof idValue === "string" && idValue.length > 0) {
|
||||
return idValue;
|
||||
}
|
||||
|
||||
const metadataValue = project.metadata;
|
||||
if (!isRecord(metadataValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const metadataId = metadataValue.id;
|
||||
if (typeof metadataId === "string" && metadataId.length > 0) {
|
||||
return metadataId;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getCurrentSceneId({
|
||||
value,
|
||||
scenes,
|
||||
}: {
|
||||
value: unknown;
|
||||
scenes: unknown[];
|
||||
}): string {
|
||||
if (typeof value === "string" && value.length > 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const mainSceneId = findMainSceneId({ scenes });
|
||||
if (mainSceneId) {
|
||||
return mainSceneId;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function findMainSceneId({ scenes }: { scenes: unknown[] }): string | null {
|
||||
for (const scene of scenes) {
|
||||
if (!isRecord(scene)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (scene.isMain === true && typeof scene.id === "string") {
|
||||
return scene.id;
|
||||
}
|
||||
}
|
||||
|
||||
for (const scene of scenes) {
|
||||
if (!isRecord(scene)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof scene.id === "string") {
|
||||
return scene.id;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function applyLegacyBookmarks({
|
||||
scenes,
|
||||
legacyBookmarks,
|
||||
}: {
|
||||
scenes: unknown[];
|
||||
legacyBookmarks: unknown[] | null;
|
||||
}): unknown[] {
|
||||
if (!legacyBookmarks || legacyBookmarks.length === 0) {
|
||||
return scenes;
|
||||
}
|
||||
|
||||
const mainSceneId = findMainSceneId({ scenes });
|
||||
|
||||
return scenes.map((scene) => {
|
||||
if (!isRecord(scene)) {
|
||||
return scene;
|
||||
}
|
||||
|
||||
if (mainSceneId && scene.id !== mainSceneId) {
|
||||
return scene;
|
||||
}
|
||||
|
||||
if (Array.isArray(scene.bookmarks) && scene.bookmarks.length > 0) {
|
||||
return scene;
|
||||
}
|
||||
|
||||
return {
|
||||
...scene,
|
||||
bookmarks: legacyBookmarks,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function getBackgroundValue({
|
||||
value,
|
||||
backgroundType,
|
||||
backgroundColor,
|
||||
blurIntensity,
|
||||
}: {
|
||||
value: unknown;
|
||||
backgroundType?: unknown;
|
||||
backgroundColor?: unknown;
|
||||
blurIntensity?: unknown;
|
||||
}): {
|
||||
type: "color" | "blur";
|
||||
color?: string;
|
||||
blurIntensity?: number;
|
||||
} {
|
||||
if (isRecord(value)) {
|
||||
const typeValue = value.type;
|
||||
if (typeValue === "blur") {
|
||||
return {
|
||||
type: "blur",
|
||||
blurIntensity: getNumberValue({
|
||||
value: value.blurIntensity,
|
||||
fallback: DEFAULT_BLUR_INTENSITY,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: "color",
|
||||
color: getStringValue({ value: value.color, fallback: DEFAULT_COLOR }),
|
||||
};
|
||||
}
|
||||
|
||||
if (backgroundType === "blur") {
|
||||
return {
|
||||
type: "blur",
|
||||
blurIntensity: getNumberValue({
|
||||
value: blurIntensity,
|
||||
fallback: DEFAULT_BLUR_INTENSITY,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: "color",
|
||||
color: getStringValue({ value: backgroundColor, fallback: DEFAULT_COLOR }),
|
||||
};
|
||||
}
|
||||
|
||||
function getCanvasSizeValue({
|
||||
value,
|
||||
fallback,
|
||||
}: {
|
||||
value: unknown;
|
||||
fallback: { width: number; height: number };
|
||||
}): { width: number; height: number } {
|
||||
if (isRecord(value)) {
|
||||
const width = getNumberValue({
|
||||
value: value.width,
|
||||
fallback: fallback.width,
|
||||
});
|
||||
const height = getNumberValue({
|
||||
value: value.height,
|
||||
fallback: fallback.height,
|
||||
});
|
||||
|
||||
return { width, height };
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function getNumberValue({
|
||||
value,
|
||||
fallback,
|
||||
}: {
|
||||
value: unknown;
|
||||
fallback: number;
|
||||
}): number {
|
||||
return typeof value === "number" ? value : fallback;
|
||||
}
|
||||
|
||||
function getStringValue({
|
||||
value,
|
||||
fallback,
|
||||
}: {
|
||||
value: unknown;
|
||||
fallback?: string;
|
||||
}): string | undefined {
|
||||
if (typeof value === "string") {
|
||||
return value;
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function normalizeDateString({ value }: { value: unknown }): string {
|
||||
if (value instanceof Date) {
|
||||
return value.toISOString();
|
||||
}
|
||||
|
||||
if (typeof value === "string") {
|
||||
return value;
|
||||
}
|
||||
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
function isV2Project({ project }: { project: ProjectRecord }): boolean {
|
||||
const versionValue = project.version;
|
||||
if (typeof versionValue === "number" && versionValue >= 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isRecord(project.metadata) && isRecord(project.settings);
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is ProjectRecord {
|
||||
return typeof value === "object" && value !== null;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import { getProjectDurationFromScenes } from "@/lib/scenes";
|
||||
import { IndexedDBAdapter } from "@/services/storage/indexeddb-adapter";
|
||||
import type { TScene } from "@/types/timeline";
|
||||
import { StorageMigration } from "./base";
|
||||
|
||||
type ProjectRecord = Record<string, unknown>;
|
||||
|
||||
export class V2toV3Migration extends StorageMigration {
|
||||
from = 2;
|
||||
to = 3;
|
||||
|
||||
async run(): Promise<void> {
|
||||
const projectsAdapter = new IndexedDBAdapter<unknown>(
|
||||
"video-editor-projects",
|
||||
"projects",
|
||||
1,
|
||||
);
|
||||
const projects = await projectsAdapter.getAll();
|
||||
|
||||
for (const project of projects) {
|
||||
if (!isRecord(project)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const projectId = getProjectId({ project });
|
||||
if (!projectId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isV3Project({ project })) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const scenes = getScenes({ project });
|
||||
const duration = getProjectDurationFromScenes({ scenes });
|
||||
|
||||
const metadataValue = project.metadata;
|
||||
const metadata = isRecord(metadataValue)
|
||||
? { ...metadataValue, duration }
|
||||
: { duration };
|
||||
|
||||
const migratedProject = {
|
||||
...project,
|
||||
metadata,
|
||||
version: 3,
|
||||
};
|
||||
|
||||
await projectsAdapter.set(projectId, migratedProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getProjectId({ project }: { project: ProjectRecord }): string | null {
|
||||
const idValue = project.id;
|
||||
if (typeof idValue === "string" && idValue.length > 0) {
|
||||
return idValue;
|
||||
}
|
||||
|
||||
const metadataValue = project.metadata;
|
||||
if (!isRecord(metadataValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const metadataId = metadataValue.id;
|
||||
if (typeof metadataId === "string" && metadataId.length > 0) {
|
||||
return metadataId;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getScenes({ project }: { project: ProjectRecord }): TScene[] {
|
||||
const scenesValue = project.scenes;
|
||||
if (!Array.isArray(scenesValue)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return scenesValue.filter(isRecord) as unknown as TScene[];
|
||||
}
|
||||
|
||||
function isV3Project({ project }: { project: ProjectRecord }): boolean {
|
||||
const versionValue = project.version;
|
||||
if (typeof versionValue === "number" && versionValue >= 3) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isRecord(project.metadata) && typeof project.metadata.duration === "number";
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is ProjectRecord {
|
||||
return typeof value === "object" && value !== null;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { IndexedDBAdapter } from "@/services/storage/indexeddb-adapter";
|
||||
|
||||
type StorageVersionRecord = {
|
||||
version: number;
|
||||
inProgress?: {
|
||||
from: number;
|
||||
to: number;
|
||||
};
|
||||
};
|
||||
|
||||
const DEFAULT_DB_NAME = "video-editor-meta";
|
||||
const DEFAULT_STORE_NAME = "storage-version";
|
||||
const DEFAULT_DB_VERSION = 1;
|
||||
const STORAGE_VERSION_KEY = "storage-version";
|
||||
|
||||
export class StorageVersionManager {
|
||||
private adapter: IndexedDBAdapter<StorageVersionRecord>;
|
||||
|
||||
constructor({
|
||||
dbName = DEFAULT_DB_NAME,
|
||||
storeName = DEFAULT_STORE_NAME,
|
||||
version = DEFAULT_DB_VERSION,
|
||||
}: {
|
||||
dbName?: string;
|
||||
storeName?: string;
|
||||
version?: number;
|
||||
} = {}) {
|
||||
this.adapter = new IndexedDBAdapter<StorageVersionRecord>(
|
||||
dbName,
|
||||
storeName,
|
||||
version,
|
||||
);
|
||||
}
|
||||
|
||||
async getVersion(): Promise<number> {
|
||||
const record = await this.adapter.get(STORAGE_VERSION_KEY);
|
||||
return record?.version ?? 0;
|
||||
}
|
||||
|
||||
async getVersionRecord(): Promise<StorageVersionRecord | null> {
|
||||
return this.adapter.get(STORAGE_VERSION_KEY);
|
||||
}
|
||||
|
||||
async setVersion({ version }: { version: number }): Promise<void> {
|
||||
const record = await this.getVersionRecord();
|
||||
const inProgress = record?.inProgress;
|
||||
await this.adapter.set(STORAGE_VERSION_KEY, {
|
||||
version,
|
||||
...(inProgress ? { inProgress } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
async setInProgress({
|
||||
from,
|
||||
to,
|
||||
}: {
|
||||
from: number;
|
||||
to: number;
|
||||
}): Promise<void> {
|
||||
const record = await this.getVersionRecord();
|
||||
const version = record?.version ?? 0;
|
||||
await this.adapter.set(STORAGE_VERSION_KEY, {
|
||||
version,
|
||||
inProgress: { from, to },
|
||||
});
|
||||
}
|
||||
|
||||
async clearInProgress(): Promise<void> {
|
||||
const record = await this.getVersionRecord();
|
||||
const version = record?.version ?? 0;
|
||||
await this.adapter.set(STORAGE_VERSION_KEY, { version });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user