fix: initialise missing compatibility markers without pausing

This commit is contained in:
vorotamoroz committed 2026-09-29 08:28:01 +00:00
1 parent b83f837065
commit 126adcb955
22 files changed
+326 -212

No files matched your search

+5 -16
View File
@@ -8,7 +8,7 @@ export const DATABASE_COMPATIBILITY_LEGACY_VERSION_KEY_PREFIX = "obsidian-live-s
export const COMPATIBILITY_PAUSE_SETTING_MESSAGE =
"Remote synchronisation is paused until this device's compatibility review has been completed.";
export type DatabaseCompatibilityVersionState = "missing" | "invalid" | "upgrade" | "downgrade";
export type DatabaseCompatibilityVersionState = "invalid" | "upgrade" | "downgrade";
export interface DatabaseCompatibilityReason {
source: "database-version";
@@ -57,18 +57,9 @@ export interface CompatibilityEvaluationInput {
function databaseVersionReason(
acknowledgedVersion: string | null,
currentVersion: number,
isNewVault: boolean
currentVersion: number
): DatabaseCompatibilityReason | undefined {
if (acknowledgedVersion === null || acknowledgedVersion === "") {
if (isNewVault) return undefined;
return {
source: "database-version",
state: "missing",
currentVersion,
resumable: true,
};
}
if (acknowledgedVersion === null || acknowledgedVersion === "") return undefined;
const parsed = Number(acknowledgedVersion);
if (!Number.isSafeInteger(parsed)) {
@@ -104,9 +95,8 @@ function databaseVersionReason(
* The caller owns persistence, user interaction, and the actual replication gate.
*/
export function evaluateCompatibilityPause(input: CompatibilityEvaluationInput): CompatibilityEvaluation {
const isNewVault = input.migrationState?.isNewVault === true;
const reasons: CompatibilityPauseReason[] = [];
const databaseReason = databaseVersionReason(input.acknowledgedVersion, input.currentVersion, isNewVault);
const databaseReason = databaseVersionReason(input.acknowledgedVersion, input.currentVersion);
if (databaseReason) reasons.push(databaseReason);
if (input.migrationState?.requiresSyncReview === true) {
@@ -130,8 +120,7 @@ export function evaluateCompatibilityPause(input: CompatibilityEvaluationInput):
if (reasons.length === 0) {
return {
initialiseAcknowledgedVersion:
isNewVault && (input.acknowledgedVersion === null || input.acknowledgedVersion === ""),
initialiseAcknowledgedVersion: input.acknowledgedVersion === null || input.acknowledgedVersion === "",
};
}
+82 -48
View File
@@ -68,8 +68,32 @@ describe("database compatibility evaluation", () => {
});
});
it("requires review when an existing Vault has no valid acknowledged version", () => {
for (const acknowledgedVersion of [null, "invalid"]) {
it.each([null, ""])(
"initialises a missing marker (%s) without pausing an existing Vault",
(acknowledgedVersion) => {
const result = evaluateCompatibilityPause({
acknowledgedVersion,
currentVersion: 12,
migrationState: migrationState(),
legacyReviewMessage: "",
});
expect(result).toEqual({ initialiseAcknowledgedVersion: true });
}
);
it("initialises a missing marker when no settings migration state is available", () => {
expect(
evaluateCompatibilityPause({
acknowledgedVersion: null,
currentVersion: 12,
legacyReviewMessage: "",
})
).toEqual({ initialiseAcknowledgedVersion: true });
});
it("requires review when an existing Vault has an invalid acknowledged version", () => {
for (const acknowledgedVersion of ["invalid", "NaN", "12.5"]) {
const result = evaluateCompatibilityPause({
acknowledgedVersion,
currentVersion: 12,
@@ -77,39 +101,44 @@ describe("database compatibility evaluation", () => {
legacyReviewMessage: "",
});
expect(result.pause?.resumable).toBe(true);
expect(result.pause?.reasons[0]).toMatchObject({ source: "database-version" });
expect(result.pause?.reasons[0]).toMatchObject({ source: "database-version", state: "invalid" });
expect(result.initialiseAcknowledgedVersion).toBe(false);
}
});
it("does not permit a future settings schema to be acknowledged by an older implementation", () => {
const result = evaluateCompatibilityPause({
acknowledgedVersion: "12",
currentVersion: 12,
migrationState: migrationState({
sourceVersion: 3,
targetVersion: 2,
isFromFutureSchema: true,
requiresSyncReview: true,
}),
legacyReviewMessage: "",
});
expect(result.pause).toEqual({
resumable: false,
reasons: [
{
source: "settings-schema",
it.each(["12", null])(
"does not permit a future settings schema with marker %s to be acknowledged",
(acknowledgedVersion) => {
const result = evaluateCompatibilityPause({
acknowledgedVersion,
currentVersion: 12,
migrationState: migrationState({
sourceVersion: 3,
currentVersion: 2,
targetVersion: 2,
isFromFutureSchema: true,
resumable: false,
reviewReasons: [],
},
],
});
});
requiresSyncReview: true,
}),
legacyReviewMessage: "",
});
it("retains a settings migration review in the host compatibility reason", () => {
expect(result.pause).toEqual({
resumable: false,
reasons: [
{
source: "settings-schema",
sourceVersion: 3,
currentVersion: 2,
isFromFutureSchema: true,
resumable: false,
reviewReasons: [],
},
],
});
expect(result.initialiseAcknowledgedVersion).toBe(false);
}
);
it.each(["12", null])("retains a settings migration review with marker %s", (acknowledgedVersion) => {
const reviewReasons = [
{
code: "legacy-update-review-pending",
@@ -118,7 +147,7 @@ describe("database compatibility evaluation", () => {
},
];
const result = evaluateCompatibilityPause({
acknowledgedVersion: "12",
acknowledgedVersion,
currentVersion: 12,
migrationState: migrationState({
sourceVersion: 9,
@@ -137,27 +166,32 @@ describe("database compatibility evaluation", () => {
resumable: true,
reviewReasons,
});
expect(result.initialiseAcknowledgedVersion).toBe(false);
});
it("compatibility: retains an earlier unstructured review when no structured reason can be reconstructed", () => {
const result = evaluateCompatibilityPause({
acknowledgedVersion: "12",
currentVersion: 12,
migrationState: migrationState(),
legacyReviewMessage: "Review an earlier compatibility change.",
});
it.each(["12", null])(
"compatibility: retains an earlier unstructured review with marker %s",
(acknowledgedVersion) => {
const result = evaluateCompatibilityPause({
acknowledgedVersion,
currentVersion: 12,
migrationState: migrationState(),
legacyReviewMessage: "Review an earlier compatibility change.",
});
expect(result.pause).toEqual({
resumable: true,
reasons: [
{
source: "legacy-review",
message: "Review an earlier compatibility change.",
resumable: true,
},
],
});
});
expect(result.pause).toEqual({
resumable: true,
reasons: [
{
source: "legacy-review",
message: "Review an earlier compatibility change.",
resumable: true,
},
],
});
expect(result.initialiseAcknowledgedVersion).toBe(false);
}
);
it("compatibility: scopes the earlier review marker to the Vault", () => {
expect(legacyDatabaseCompatibilityVersionKey("Example Vault")).toBe("obsidian-live-sync-verExample Vault");