2024-10-05 23:17:20 +02:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) Contributors to the Suwayomi project
|
|
|
|
|
*
|
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
|
*/
|
|
|
|
|
|
2026-03-11 00:11:29 +01:00
|
|
|
import type { useEffect } from 'react';
|
2025-01-05 02:15:03 +01:00
|
|
|
import {
|
|
|
|
|
APP_METADATA_KEY_PREFIX,
|
|
|
|
|
METADATA_MIGRATIONS,
|
|
|
|
|
VALID_APP_METADATA_KEYS,
|
2025-08-15 22:02:58 +02:00
|
|
|
} from '@/features/metadata/Metadata.constants.ts';
|
2026-03-11 00:11:29 +01:00
|
|
|
import type {
|
2025-01-05 02:15:03 +01:00
|
|
|
AppMetadataKeys,
|
|
|
|
|
IMetadataMigration,
|
|
|
|
|
Metadata,
|
|
|
|
|
MetadataHolder,
|
|
|
|
|
MetadataHolderType,
|
|
|
|
|
MetadataKeyValuePair,
|
2025-08-15 22:02:58 +02:00
|
|
|
} from '@/features/metadata/Metadata.types.ts';
|
|
|
|
|
import { extractOriginalKey, getMetadataKey } from '@/features/metadata/Metadata.utils.ts';
|
2026-03-11 00:11:29 +01:00
|
|
|
import type { MangaIdInfo } from '@/features/manga/Manga.types.ts';
|
|
|
|
|
import type { CategoryIdInfo } from '@/features/category/Category.types.ts';
|
2025-01-05 02:15:03 +01:00
|
|
|
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
2026-01-25 20:32:56 +01:00
|
|
|
import { getMetadataUpdateFunction } from '@/features/metadata/services/MetadataUpdater.ts';
|
2026-01-27 23:19:37 +01:00
|
|
|
import { MetadataChunker } from '@/features/metadata/services/MetadataChunker.ts';
|
2026-03-11 00:11:29 +01:00
|
|
|
import type { SourceIdInfo } from '@/features/source/Source.types.ts';
|
|
|
|
|
import type { ChapterIdInfo } from '@/features/chapter/Chapter.types.ts';
|
2024-10-05 23:17:20 +02:00
|
|
|
|
2025-01-05 14:54:20 +01:00
|
|
|
const getAppKeyPrefixForMigration = (migrationId: number): string => {
|
2024-10-05 23:17:20 +02:00
|
|
|
const appKeyPrefix = METADATA_MIGRATIONS.slice(0, migrationId)
|
|
|
|
|
.reverse()
|
|
|
|
|
.find((migration) => !!migration.appKeyPrefix);
|
|
|
|
|
|
|
|
|
|
return appKeyPrefix?.appKeyPrefix?.newPrefix ?? APP_METADATA_KEY_PREFIX;
|
|
|
|
|
};
|
|
|
|
|
|
2025-01-04 21:27:51 +01:00
|
|
|
const getAppMetadataFrom = (
|
|
|
|
|
meta: Metadata,
|
|
|
|
|
prefixes: string[] = [],
|
|
|
|
|
appPrefix: string = APP_METADATA_KEY_PREFIX,
|
|
|
|
|
): Metadata => {
|
|
|
|
|
const appMetadata: Metadata = {};
|
|
|
|
|
|
|
|
|
|
Object.entries(meta).forEach(([key, value]) => {
|
|
|
|
|
if (key.startsWith([appPrefix, ...prefixes].join('_'))) {
|
|
|
|
|
appMetadata[key] = value;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return appMetadata;
|
|
|
|
|
};
|
|
|
|
|
|
2025-01-05 14:54:20 +01:00
|
|
|
const applyAppKeyPrefixMigration = (meta: Metadata, migration: IMetadataMigration): Metadata => {
|
2024-10-05 23:17:20 +02:00
|
|
|
const migratedMetadata: Metadata = { ...meta };
|
|
|
|
|
|
|
|
|
|
if (!migration.appKeyPrefix) {
|
|
|
|
|
return migratedMetadata;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { oldPrefix, newPrefix } = migration.appKeyPrefix;
|
|
|
|
|
|
2024-10-28 18:03:29 +01:00
|
|
|
const oldAppMetadata = getAppMetadataFrom(meta, undefined, oldPrefix);
|
2024-10-05 23:17:20 +02:00
|
|
|
|
2025-01-06 12:23:41 +01:00
|
|
|
Object.keys(oldAppMetadata).forEach((oldKey) => {
|
|
|
|
|
const keyWithNewPrefix = oldKey.replace(oldPrefix, newPrefix);
|
|
|
|
|
migratedMetadata[keyWithNewPrefix] = oldAppMetadata[oldKey];
|
|
|
|
|
});
|
2024-10-05 23:17:20 +02:00
|
|
|
|
|
|
|
|
return migratedMetadata;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const applyMetadataValueMigration = (meta: Metadata, migration: IMetadataMigration, appKeyPrefix: string): Metadata => {
|
|
|
|
|
const migratedMetadata: Metadata = { ...meta };
|
|
|
|
|
|
|
|
|
|
if (!migration.values) {
|
|
|
|
|
return migratedMetadata;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 18:03:29 +01:00
|
|
|
const appMetadata = getAppMetadataFrom(meta, undefined, appKeyPrefix);
|
2024-10-05 23:17:20 +02:00
|
|
|
const metadataValueChanges = migration.values;
|
|
|
|
|
|
|
|
|
|
metadataValueChanges.forEach(({ key, oldValue, newValue }) => {
|
|
|
|
|
const migrateValue = (metaKey: string) => {
|
2025-05-17 21:04:44 +02:00
|
|
|
if (
|
|
|
|
|
(oldValue === undefined && meta[metaKey] === oldValue) ||
|
|
|
|
|
(oldValue !== undefined && meta[metaKey].match(oldValue))
|
|
|
|
|
) {
|
2026-01-25 17:31:02 +01:00
|
|
|
migratedMetadata[metaKey] =
|
|
|
|
|
typeof newValue === 'function' ? newValue(meta[metaKey], metaKey, appMetadata) : newValue;
|
2024-10-05 23:17:20 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const migrateValueOfAllAppKeys = key === undefined;
|
2025-01-05 13:31:07 +01:00
|
|
|
Object.keys(appMetadata).forEach((metaKey) => {
|
|
|
|
|
if (migrateValueOfAllAppKeys || metaKey.endsWith(key)) {
|
2024-10-05 23:17:20 +02:00
|
|
|
migrateValue(metaKey);
|
2025-01-05 13:31:07 +01:00
|
|
|
}
|
|
|
|
|
});
|
2024-10-05 23:17:20 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return migratedMetadata;
|
|
|
|
|
};
|
|
|
|
|
|
2025-01-05 14:00:50 +01:00
|
|
|
const applyMetadataKeyMigration = (meta: Metadata, migration: IMetadataMigration, appKeyPrefix: string): Metadata => {
|
2024-10-05 23:17:20 +02:00
|
|
|
const migratedMetadata: Metadata = { ...meta };
|
|
|
|
|
|
|
|
|
|
if (!migration.keys) {
|
|
|
|
|
return migratedMetadata;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-05 14:00:50 +01:00
|
|
|
const appMetadata = getAppMetadataFrom(meta, undefined, appKeyPrefix);
|
2024-10-05 23:17:20 +02:00
|
|
|
|
2025-01-05 14:00:50 +01:00
|
|
|
migration.keys.forEach(({ oldKey, newKey }) => {
|
|
|
|
|
Object.keys(appMetadata).forEach((key) => {
|
|
|
|
|
if (!key.endsWith(oldKey)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-10-05 23:17:20 +02:00
|
|
|
|
2025-01-05 14:00:50 +01:00
|
|
|
const prefixes = key.split('_');
|
|
|
|
|
const prefix = prefixes.slice(0, prefixes.length - 1).join('_');
|
|
|
|
|
|
|
|
|
|
const newKeyWithOldKeysPrefix = `${prefix}_${newKey}`;
|
2024-10-05 23:17:20 +02:00
|
|
|
|
2025-01-05 14:00:50 +01:00
|
|
|
migratedMetadata[newKeyWithOldKeysPrefix] = appMetadata[key];
|
|
|
|
|
});
|
2024-10-05 23:17:20 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return migratedMetadata;
|
|
|
|
|
};
|
|
|
|
|
|
2025-01-05 14:15:29 +01:00
|
|
|
const applyMetadataDeleteKeysMigration = (
|
|
|
|
|
meta: Metadata,
|
|
|
|
|
migration: IMetadataMigration,
|
|
|
|
|
appKeyPrefix: string,
|
|
|
|
|
): Metadata => {
|
|
|
|
|
const migratedMetadata: Metadata = {};
|
|
|
|
|
|
|
|
|
|
if (!migration.deleteKeys) {
|
2025-01-06 01:58:49 +01:00
|
|
|
return { ...meta };
|
2025-01-05 14:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const appMetadata = getAppMetadataFrom(meta, undefined, appKeyPrefix);
|
|
|
|
|
|
2025-01-06 12:39:39 +01:00
|
|
|
Object.keys(appMetadata).forEach((key) => {
|
|
|
|
|
if (migration.deleteKeys?.includes(extractOriginalKey(key))) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-01-05 14:15:29 +01:00
|
|
|
|
2025-01-06 12:39:39 +01:00
|
|
|
migratedMetadata[key] = appMetadata[key];
|
2025-01-05 14:15:29 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return migratedMetadata;
|
|
|
|
|
};
|
|
|
|
|
|
2025-01-11 16:00:16 +01:00
|
|
|
const getOutdatedMetadataKeys = (metadata: Metadata | undefined, migrationId: number): AppMetadataKeys[] => {
|
2025-01-05 02:15:03 +01:00
|
|
|
if (!metadata) {
|
|
|
|
|
return [];
|
2024-10-05 23:17:20 +02:00
|
|
|
}
|
|
|
|
|
|
2025-01-11 16:00:16 +01:00
|
|
|
const oldAppKeyPrefixes = METADATA_MIGRATIONS.slice(migrationId).reduce((acc, migration) => {
|
2025-01-05 02:15:03 +01:00
|
|
|
const oldPrefix = migration.appKeyPrefix?.oldPrefix;
|
|
|
|
|
if (!oldPrefix) {
|
|
|
|
|
return acc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [...acc, oldPrefix];
|
|
|
|
|
}, [] as string[]);
|
|
|
|
|
|
2025-01-11 16:00:16 +01:00
|
|
|
const keyToDeleteInMigrations = METADATA_MIGRATIONS.slice(migrationId).reduce(
|
2025-01-05 14:15:29 +01:00
|
|
|
(acc, migration) => [...acc, ...(migration.deleteKeys ?? [])],
|
|
|
|
|
[] as string[],
|
|
|
|
|
);
|
|
|
|
|
|
2025-01-05 02:15:03 +01:00
|
|
|
return Object.keys(metadata).filter((key) => {
|
2026-03-14 02:10:25 +01:00
|
|
|
const [appKeyPrefixOfKey] = key.split('_');
|
2025-01-05 02:15:03 +01:00
|
|
|
|
|
|
|
|
const isMetadataKeyOfApp = [...oldAppKeyPrefixes, APP_METADATA_KEY_PREFIX].includes(appKeyPrefixOfKey);
|
|
|
|
|
if (!isMetadataKeyOfApp) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isOldKeyPrefix = oldAppKeyPrefixes.includes(appKeyPrefixOfKey);
|
|
|
|
|
if (isOldKeyPrefix) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-05 14:15:29 +01:00
|
|
|
const extractedKey = extractOriginalKey(key);
|
|
|
|
|
|
|
|
|
|
if (keyToDeleteInMigrations.includes(extractedKey)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return !VALID_APP_METADATA_KEYS.includes(extractedKey);
|
2025-01-05 02:15:03 +01:00
|
|
|
}) as AppMetadataKeys[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getNewMetadataKeys = (
|
|
|
|
|
metadata: Metadata | undefined,
|
|
|
|
|
migratedMetadata: Metadata,
|
|
|
|
|
metadataKeyToDelete: string[],
|
2025-01-11 16:00:16 +01:00
|
|
|
migrationId: number,
|
2025-01-05 02:15:03 +01:00
|
|
|
): string[] => {
|
|
|
|
|
if (!metadata) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-11 16:00:16 +01:00
|
|
|
const newKeys = METADATA_MIGRATIONS.slice(migrationId).reduce((acc, migration) => {
|
2025-01-05 02:15:03 +01:00
|
|
|
if (!migration.keys) {
|
|
|
|
|
return acc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const keys = migration.keys.map(({ newKey }) => newKey).filter((key) => key !== undefined);
|
|
|
|
|
|
|
|
|
|
return [...acc, ...keys];
|
|
|
|
|
}, [] as string[]);
|
|
|
|
|
|
|
|
|
|
return Object.keys(migratedMetadata).filter((metadataKey) => {
|
|
|
|
|
const key = extractOriginalKey(metadataKey);
|
|
|
|
|
|
|
|
|
|
const isNewKeyOfAMigration = newKeys.includes(key);
|
|
|
|
|
const isOldKeyOfAMigration = metadataKeyToDelete.includes(key);
|
|
|
|
|
const isAlreadyCommited = Object.hasOwn(metadata, metadataKey);
|
|
|
|
|
|
|
|
|
|
return !isAlreadyCommited && isNewKeyOfAMigration && !isOldKeyOfAMigration;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2025-01-11 03:02:10 +01:00
|
|
|
const getMetadataKeysWithUpdatedValues = (
|
|
|
|
|
metadata: Metadata | undefined,
|
|
|
|
|
newAndDeletedMetadataKeys: string[],
|
2025-01-11 16:00:16 +01:00
|
|
|
migrationId: number,
|
2025-01-11 03:02:10 +01:00
|
|
|
): string[] => {
|
|
|
|
|
if (!metadata) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-11 16:00:16 +01:00
|
|
|
const keysWithUpdatedValues = METADATA_MIGRATIONS.slice(migrationId).reduce((acc, migration) => {
|
2025-01-11 03:02:10 +01:00
|
|
|
const keysWithUpdatedValuesOfMigration = Object.keys(metadata).filter((metadataKey) =>
|
|
|
|
|
migration.values?.some(({ key: migrationKey, oldValue }) => {
|
|
|
|
|
const isMigrationForAllKeys = !migrationKey;
|
|
|
|
|
const doesValueMatch = metadata[metadataKey] === oldValue;
|
|
|
|
|
|
|
|
|
|
if (isMigrationForAllKeys) {
|
|
|
|
|
return doesValueMatch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return metadataKey.endsWith(migrationKey) && doesValueMatch;
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return [...acc, ...keysWithUpdatedValuesOfMigration];
|
|
|
|
|
}, [] as string[]);
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
...new Set([
|
|
|
|
|
...keysWithUpdatedValues.filter(
|
|
|
|
|
(keyWithUpdatedValue) => !newAndDeletedMetadataKeys.includes(keyWithUpdatedValue),
|
|
|
|
|
),
|
|
|
|
|
]),
|
|
|
|
|
];
|
|
|
|
|
};
|
|
|
|
|
|
2025-01-05 02:15:03 +01:00
|
|
|
/**
|
|
|
|
|
* Prevent spamming requests due to frequent metadata reads while the migration hasn't been commited to the server yet
|
|
|
|
|
*/
|
|
|
|
|
const commitedMigrations = new Set<string>();
|
|
|
|
|
const commitMigratedMetadata = (
|
|
|
|
|
type: MetadataHolderType,
|
|
|
|
|
metadataHolder:
|
|
|
|
|
| MetadataHolder
|
|
|
|
|
| (MangaIdInfo & MetadataHolder)
|
|
|
|
|
| (ChapterIdInfo & MetadataHolder)
|
|
|
|
|
| (CategoryIdInfo & MetadataHolder)
|
2025-04-21 17:00:13 +02:00
|
|
|
| (SourceIdInfo & MetadataHolder)
|
2025-01-05 02:15:03 +01:00
|
|
|
| undefined,
|
|
|
|
|
migratedMetadata: Metadata,
|
|
|
|
|
useEffectFn: typeof useEffect = (fn: () => void) => fn(),
|
|
|
|
|
): void => {
|
2026-01-27 23:19:37 +01:00
|
|
|
const rawMetadata = metadataHolder?.meta;
|
|
|
|
|
const metadata = MetadataChunker.reassembleAllChunkedValues(rawMetadata);
|
2025-01-05 02:15:03 +01:00
|
|
|
|
2025-01-11 16:00:16 +01:00
|
|
|
const migrationId = Number(metadata?.[getMetadataKey('migration')] ?? 1);
|
|
|
|
|
|
|
|
|
|
const metadataKeysToDelete = getOutdatedMetadataKeys(metadata, migrationId);
|
|
|
|
|
const newMetadataKeys = getNewMetadataKeys(metadata, migratedMetadata, metadataKeysToDelete, migrationId);
|
|
|
|
|
const metadataKeysWithUpdatedValues = getMetadataKeysWithUpdatedValues(
|
|
|
|
|
metadata,
|
|
|
|
|
[...metadataKeysToDelete, ...newMetadataKeys],
|
|
|
|
|
migrationId,
|
|
|
|
|
);
|
2025-01-11 03:02:10 +01:00
|
|
|
const metadataToUpdate = [...newMetadataKeys, ...metadataKeysWithUpdatedValues].map((key) => [
|
|
|
|
|
key,
|
|
|
|
|
migratedMetadata[key],
|
|
|
|
|
]) as MetadataKeyValuePair[];
|
2025-01-05 02:15:03 +01:00
|
|
|
|
2026-01-27 23:19:37 +01:00
|
|
|
// Expand app keys to chunk keys for deletion (e.g., some_key -> some_key_0, some_key_1, some_key_2, ..., some_key_n, some_key_length
|
|
|
|
|
const keysToDelete = metadataKeysToDelete.flatMap((fullKey) => {
|
|
|
|
|
if (rawMetadata && MetadataChunker.isChunkedInMetadata(rawMetadata, fullKey)) {
|
|
|
|
|
const count = MetadataChunker.getExistingChunkCount(rawMetadata, fullKey);
|
|
|
|
|
const chunkKeys = Array.from({ length: count }, (_, i) => MetadataChunker.getChunkIndexKey(fullKey, i));
|
|
|
|
|
return [MetadataChunker.getChunkLengthKey(fullKey), ...chunkKeys];
|
|
|
|
|
}
|
|
|
|
|
return [fullKey];
|
|
|
|
|
}) as AppMetadataKeys[];
|
|
|
|
|
|
2025-01-05 02:15:03 +01:00
|
|
|
const updateMetadata = getMetadataUpdateFunction(type, metadataHolder ?? { id: -1, meta: {} });
|
|
|
|
|
|
|
|
|
|
useEffectFn(() => {
|
|
|
|
|
(async () => {
|
2025-01-11 03:13:38 +01:00
|
|
|
const itemMigrationKey = `${type}_${type === 'global' ? '' : (metadataHolder as { id: any } | undefined)?.id}`;
|
2025-01-05 02:15:03 +01:00
|
|
|
|
|
|
|
|
const commitMigration = !commitedMigrations.has(itemMigrationKey);
|
|
|
|
|
if (!commitMigration) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-11 16:00:16 +01:00
|
|
|
const isMetadataAlreadyMigrated = !metadata || migrationId === METADATA_MIGRATIONS.length;
|
2025-01-05 02:15:03 +01:00
|
|
|
|
2026-04-07 15:11:12 +02:00
|
|
|
const isCommitRequired = !isMetadataAlreadyMigrated;
|
2025-01-05 02:15:03 +01:00
|
|
|
if (!isCommitRequired) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
commitedMigrations.add(itemMigrationKey);
|
|
|
|
|
|
|
|
|
|
try {
|
2026-01-25 20:32:56 +01:00
|
|
|
await updateMetadata({
|
|
|
|
|
update: metadataToUpdate,
|
2026-01-27 23:19:37 +01:00
|
|
|
delete: keysToDelete,
|
2026-01-25 20:32:56 +01:00
|
|
|
migrate: [['migration', METADATA_MIGRATIONS.length]],
|
|
|
|
|
isMetadataKey: true,
|
|
|
|
|
});
|
2025-01-05 02:15:03 +01:00
|
|
|
} catch (e) {
|
|
|
|
|
commitedMigrations.delete(itemMigrationKey);
|
|
|
|
|
|
|
|
|
|
defaultPromiseErrorHandler(
|
|
|
|
|
`MetadataMigrations#commitMigrateMetadata(${type}, ${(metadataHolder as { id: any })?.id})`,
|
2025-03-03 23:59:03 +01:00
|
|
|
)(e);
|
2025-01-05 02:15:03 +01:00
|
|
|
}
|
|
|
|
|
})();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const applyMetadataMigrations = (
|
|
|
|
|
type: MetadataHolderType,
|
|
|
|
|
metadataHolder?:
|
|
|
|
|
| MetadataHolder
|
|
|
|
|
| (MangaIdInfo & MetadataHolder)
|
|
|
|
|
| (ChapterIdInfo & MetadataHolder)
|
|
|
|
|
| (CategoryIdInfo & MetadataHolder)
|
2025-04-21 17:00:13 +02:00
|
|
|
| (SourceIdInfo & MetadataHolder),
|
2025-01-05 02:15:03 +01:00
|
|
|
useEffectFn: typeof useEffect = (fn: () => void) => fn(),
|
|
|
|
|
): Metadata | undefined => {
|
2026-01-27 23:19:37 +01:00
|
|
|
const rawMeta = metadataHolder?.meta ?? {};
|
|
|
|
|
const meta = MetadataChunker.reassembleAllChunkedValues(rawMeta) ?? {};
|
2025-01-05 02:15:03 +01:00
|
|
|
|
2025-01-05 14:53:52 +01:00
|
|
|
const migrationIdKey = getMetadataKey('migration');
|
|
|
|
|
const appliedMigrationId = Number.isNaN(Number(meta[migrationIdKey]))
|
|
|
|
|
? 0
|
2025-01-06 01:44:33 +01:00
|
|
|
: Math.max(0, Number(meta[migrationIdKey]));
|
2025-01-05 02:15:03 +01:00
|
|
|
|
|
|
|
|
const migrationToMetadata: [number, Metadata][] = [[0, meta]];
|
2024-10-05 23:17:20 +02:00
|
|
|
|
|
|
|
|
METADATA_MIGRATIONS.forEach((migration, index) => {
|
|
|
|
|
const migrationId = index + 1;
|
2026-03-14 02:10:25 +01:00
|
|
|
const [, metadataToMigrate] = migrationToMetadata[migrationId - 1];
|
2025-01-05 02:15:03 +01:00
|
|
|
|
|
|
|
|
const isMigrationRequired = appliedMigrationId < migrationId;
|
|
|
|
|
if (!isMigrationRequired) {
|
|
|
|
|
migrationToMetadata.push([migrationId, metadataToMigrate]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-05 14:00:50 +01:00
|
|
|
const appKeyPrefixForMigration = getAppKeyPrefixForMigration(migrationId);
|
2024-10-05 23:17:20 +02:00
|
|
|
const appKeyPrefixMigrated = applyAppKeyPrefixMigration(metadataToMigrate, migration);
|
|
|
|
|
const metadataValuesMigrated = applyMetadataValueMigration(
|
|
|
|
|
appKeyPrefixMigrated,
|
|
|
|
|
migration,
|
2025-01-05 14:00:50 +01:00
|
|
|
appKeyPrefixForMigration,
|
|
|
|
|
);
|
|
|
|
|
const metadataKeysMigrated = applyMetadataKeyMigration(
|
|
|
|
|
metadataValuesMigrated,
|
|
|
|
|
migration,
|
|
|
|
|
appKeyPrefixForMigration,
|
2024-10-05 23:17:20 +02:00
|
|
|
);
|
2025-01-05 14:15:29 +01:00
|
|
|
const metadataKeysDeletedMigrated = applyMetadataDeleteKeysMigration(
|
|
|
|
|
metadataKeysMigrated,
|
|
|
|
|
migration,
|
|
|
|
|
appKeyPrefixForMigration,
|
|
|
|
|
);
|
2024-10-05 23:17:20 +02:00
|
|
|
|
2025-01-05 14:15:29 +01:00
|
|
|
migrationToMetadata.push([migrationId, metadataKeysDeletedMigrated]);
|
2024-10-05 23:17:20 +02:00
|
|
|
});
|
|
|
|
|
|
2025-01-05 02:15:03 +01:00
|
|
|
const migratedMetadata = migrationToMetadata.pop()?.[1] ?? meta;
|
2024-10-05 23:17:20 +02:00
|
|
|
|
2025-01-05 02:15:03 +01:00
|
|
|
commitMigratedMetadata(type, metadataHolder, migratedMetadata, useEffectFn);
|
|
|
|
|
|
|
|
|
|
return migratedMetadata;
|
2024-10-05 23:17:20 +02:00
|
|
|
};
|