Add independent ID key configuration and recovery

This commit is contained in:
vorotamoroz committed 2026-09-29 12:58:11 +00:00
1 parent 126d6eadb8
commit bf7822f20b
51 files changed
+2572 -168

No files matched your search

@@ -99,6 +99,17 @@ function resolutionSettingsSignature(settings: ObsidianLiveSyncSettings): string
}
export class ModuleResolvingMismatchedTweaks extends AbstractModule {
private requiresIdConfigurationReview(assessment: TweakAssessment): boolean {
if (!assessment.entries.some(({ key, relation }) => key === "idDerivationVersion" && relation === "different")) {
return false;
}
Logger(
"The document ID configurations differ. Import the correct Setup URI, or configure the matching ID key, before synchronising.",
LOG_LEVEL_NOTICE
);
return true;
}
private _selectNewerTweakSide(current: TweakValues, preferred: Partial<TweakValues>): "REMOTE" | "CURRENT" {
Logger(`Modified: ${current.tweakModified} (current) vs ${preferred.tweakModified} (preferred)`);
const currentModified = current.tweakModified;
@@ -196,6 +207,7 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule {
assessment = assessTweakCompatibility(this.settings, preferred)
): Promise<[TweakValues | boolean, boolean]> {
if (assessment.alignment === "matched") return [false, false];
if (this.requiresIdConfigurationReview(assessment)) return [false, false];
const acceptedSettings = settingsAfterAdoption(assessment, "adoptPreferred");
const autoAcceptSide = await this._shouldAutoAcceptCompatibleLossy(assessment);
if (autoAcceptSide === "REMOTE") return [acceptedSettings, false];
@@ -363,6 +375,7 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule {
const trialSignature = JSON.stringify(trialSetting);
const currentSignature = resolutionSettingsSignature(this.settings);
const assessment = assessTweakCompatibility(trialSetting, preferred);
if (this.requiresIdConfigurationReview(assessment)) return { result: false, requireFetch: false };
if (assessment.alignment === "matched") {
this._log("The settings in the remote database are the same as the local database.", LOG_LEVEL_NOTICE);
return { result: false, requireFetch: false };
@@ -7,7 +7,7 @@ import {
type TweakValues,
} from "@vrtmrz/livesync-commonlib/compat/common/types";
import { extractObject } from "octagonal-wheels/object";
import { assessTweakCompatibility } from "@vrtmrz/livesync-commonlib/settings";
import { assessTweakCompatibility, configuredIdKey } from "@vrtmrz/livesync-commonlib/settings";
import { ModuleResolvingMismatchedTweaks } from "./ModuleResolveMismatchedTweaks";
import { setLang } from "@/common/translation";
import {
@@ -74,6 +74,68 @@ function createModule(settingsOverride: Partial<typeof DEFAULT_SETTINGS> = {}) {
}
describe("ModuleResolvingMismatchedTweaks", () => {
it.each([0, 1] as const)(
"keeps ID configuration %s when automatically aligning Chunk settings",
async (idDerivationVersion) => {
const idDerivationKey = idDerivationVersion === 1 ? "ab".repeat(32) : "";
const { module, core, askSelectStringDialogue } = createModule({
encrypt: true,
usePathObfuscation: false,
idDerivationVersion,
idDerivationKey,
autoAcceptCompatibleTweak: true,
hashAlg: "xxhash64",
tweakModified: 1,
});
const preferred: TweakValues = {
...extractObject(TweakValuesTemplate, core.settings),
idDerivationVersion: idDerivationVersion === 1 ? 0 : 1,
hashAlg: "xxhash32",
tweakModified: 2,
};
core._services.tweakValue = {
checkAndAskResolvingMismatched: module._checkAndAskResolvingMismatchedTweaks.bind(module),
};
core._services.setting.saveSettingData.mockImplementation(async () => {
configuredIdKey(core.settings);
});
await expect(module._askResolvingMismatchedTweaks(preferred, async () => true)).resolves.toBe("CHECKAGAIN");
expect(core.settings).toMatchObject({ idDerivationVersion, idDerivationKey, hashAlg: "xxhash32" });
expect(askSelectStringDialogue).not.toHaveBeenCalled();
}
);
it.each(["active", "trial"] as const)(
"withholds ordinary tweak adoption for different document ID modes (%s)",
async (route) => {
const { module, core, askSelectStringDialogue } = createModule({
encrypt: true,
usePathObfuscation: true,
idDerivationVersion: 0,
idDerivationKey: "",
});
const preferred: TweakValues = {
...extractObject(TweakValuesTemplate, core.settings),
idDerivationVersion: 1,
};
if (route === "active") {
await expect(module._checkAndAskResolvingMismatchedTweaks(preferred)).resolves.toEqual([false, false]);
} else {
await expect(module._askUseRemoteConfiguration(core.settings, preferred)).resolves.toEqual({
result: false,
requireFetch: false,
});
}
expect(askSelectStringDialogue).not.toHaveBeenCalled();
expect(core._services.setting.saveSettingData).not.toHaveBeenCalled();
expect(core.settings).toMatchObject({ idDerivationVersion: 0, idDerivationKey: "" });
}
);
it("compatibility: offers ordinary application for a missing legacy filename-case setting", async () => {
const { module, askSelectStringDialogue } = createModule({
autoAcceptCompatibleTweak: false,