@vrtmrz/livesync-commonlib 0.1.23 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/models/tweak.compatibility.d.ts +34 -0
- package/dist/common/models/tweak.compatibility.js +110 -0
- package/dist/common/models/tweak.definition.d.ts +105 -19
- package/dist/common/models/tweak.definition.js +62 -29
- package/dist/common/types.d.ts +3 -0
- package/dist/pouchdb/LiveSyncDBFunctions.d.ts +3 -3
- package/dist/pouchdb/LiveSyncDBFunctions.js +9 -15
- package/dist/replication/CentralCompatibility.d.ts +4 -2
- package/dist/replication/CentralCompatibility.js +5 -3
- package/dist/replication/couchdb/LiveSyncReplicator.js +10 -2
- package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +2 -2
- package/dist/replication/journal/LiveSyncJournalReplicator.js +18 -4
- package/dist/replication/trystero/TrysteroReplicator.js +3 -4
- package/dist/services/base/IService.d.ts +3 -3
- package/dist/services/base/ReplicationService.d.ts +2 -2
- package/dist/services/base/ReplicationService.js +14 -11
- package/dist/services/base/TweakValueService.d.ts +5 -3
- package/dist/services/implements/injectable/InjectableTweakValueService.d.ts +5 -5
- package/dist/settings.d.ts +3 -0
- package/dist/settings.js +2 -0
- package/dist/worker/bgWorker.js +1 -1
- package/docs/proven-in-use.md +2 -0
- package/docs/settings-lifecycle.md +20 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TweakValuesShouldMatchedTemplate } from "../../common/types.js";
|
|
2
|
+
import { assessTweakCompatibility } from "../../common/models/tweak.compatibility.js";
|
|
2
3
|
import {
|
|
3
4
|
LOG_LEVEL_INFO,
|
|
4
5
|
LOG_LEVEL_NOTICE,
|
|
@@ -17,7 +18,6 @@ import {
|
|
|
17
18
|
} from "./TrysteroReplicatorP2PServer.js";
|
|
18
19
|
import { encryptWithEphemeralSalt, decryptWithEphemeralSalt } from "octagonal-wheels/encryption/hkdf";
|
|
19
20
|
import { sha1 } from "octagonal-wheels/hash/purejs";
|
|
20
|
-
import { isObjectDifferent } from "octagonal-wheels/object";
|
|
21
21
|
import { getRelaySockets, pauseRelayReconnection, resumeRelayReconnection } from "@trystero-p2p/nostr";
|
|
22
22
|
import { P2PAutomationCoordinator } from "./P2PAutomationCoordinator.js";
|
|
23
23
|
import { compatGlobal } from "../../common/coreEnvFunctions.js";
|
|
@@ -728,9 +728,6 @@ ${info.lastSeq} / ${info.maxSeqInBatch})`,
|
|
|
728
728
|
const tweakValues = await connection.invokeRemoteObjectFunction("getTweakSettings", [this.server.serverPeerId], 5e3, signal);
|
|
729
729
|
if (signal?.aborted) return false;
|
|
730
730
|
const thisTweakValues = await this.getTweakSettings("");
|
|
731
|
-
if (!isObjectDifferent(thisTweakValues, tweakValues)) {
|
|
732
|
-
return true;
|
|
733
|
-
}
|
|
734
731
|
if (thisTweakValues.passphrase !== tweakValues.passphrase) {
|
|
735
732
|
Logger(
|
|
736
733
|
"Replication cancelled: Passphrase is not matched\nCannot replicate to a remote database until the problem is resolved.",
|
|
@@ -738,6 +735,8 @@ ${info.lastSeq} / ${info.maxSeqInBatch})`,
|
|
|
738
735
|
);
|
|
739
736
|
return false;
|
|
740
737
|
}
|
|
738
|
+
const tweakAssessment = assessTweakCompatibility(thisTweakValues, tweakValues);
|
|
739
|
+
if (!tweakAssessment.representationDiffers) return true;
|
|
741
740
|
Logger(
|
|
742
741
|
"Some mismatched configuration have been detected... Please check settings for efficient replication.",
|
|
743
742
|
logLevel
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FetchHttpHandler } from "@smithy/fetch-http-handler";
|
|
2
2
|
import { type LOG_LEVEL } from "octagonal-wheels/common/logger";
|
|
3
|
-
import type { AnyEntry, AUTO_MERGED, CouchDBCredentials, diff_result, DocumentID, EntryDoc, EntryHasPath, FileEventItem, FilePath, FilePathWithPrefix, LoadedEntry, MetaEntry, MISSING_OR_ERROR, ObsidianLiveSyncSettings, RemoteDBSettings, RemotePreferredTweakResult, SettingsMigrationState, TweakValues, UXFileInfo, UXFileInfoStub } from "../../common/types.js";
|
|
3
|
+
import type { AnyEntry, AUTO_MERGED, CouchDBCredentials, diff_result, DocumentID, EntryDoc, EntryHasPath, FileEventItem, FilePath, FilePathWithPrefix, LoadedEntry, MetaEntry, MISSING_OR_ERROR, ObsidianLiveSyncSettings, RemoteDBSettings, RemotePreferredTweakResult, SettingsMigrationState, TweakAssessment, TweakValues, UXFileInfo, UXFileInfoStub } from "../../common/types.js";
|
|
4
4
|
import type { LiveSyncLocalDB } from "../../pouchdb/LiveSyncLocalDB.js";
|
|
5
5
|
import type { ReplicatorInstance } from "../../replication/ReplicatorInstance.js";
|
|
6
6
|
import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase";
|
|
@@ -335,8 +335,8 @@ export interface ISettingService {
|
|
|
335
335
|
}
|
|
336
336
|
export interface ITweakValueService {
|
|
337
337
|
fetchRemotePreferred(trialSetting: RemoteDBSettings): Promise<RemotePreferredTweakResult>;
|
|
338
|
-
checkAndAskResolvingMismatched(preferred: Partial<TweakValues
|
|
339
|
-
askResolvingMismatched(preferredSource: TweakValues, updatePreferredRemote?: (setting: ObsidianLiveSyncSettings) => Promise<boolean
|
|
338
|
+
checkAndAskResolvingMismatched(preferred: Partial<TweakValues>, assessment?: TweakAssessment): Promise<[TweakValues | boolean, boolean]>;
|
|
339
|
+
askResolvingMismatched(preferredSource: TweakValues, updatePreferredRemote?: (setting: ObsidianLiveSyncSettings) => Promise<boolean>, assessment?: TweakAssessment): Promise<"OK" | "CHECKAGAIN" | "IGNORE">;
|
|
340
340
|
checkAndAskUseRemoteConfiguration(settings: RemoteDBSettings): Promise<{
|
|
341
341
|
result: false | TweakValues;
|
|
342
342
|
requireFetch: boolean;
|
|
@@ -117,8 +117,8 @@ export declare abstract class ReplicationService<T extends ServiceContext = Serv
|
|
|
117
117
|
*
|
|
118
118
|
* Each attempt owns a separate reservation. Compatibility recovery runs
|
|
119
119
|
* between those reservations, and a retry is admitted only when the exact
|
|
120
|
-
* first-attempt context remains active.
|
|
121
|
-
*
|
|
120
|
+
* first-attempt context remains active. Each attempt uses a detached settings
|
|
121
|
+
* snapshot, and a compatibility retry captures its snapshot after recovery.
|
|
122
122
|
*/
|
|
123
123
|
private performDirectionalReplication;
|
|
124
124
|
replicateAllToRemote(showingNotice?: boolean): Promise<boolean>;
|
|
@@ -329,40 +329,43 @@ class ReplicationService extends ServiceBase {
|
|
|
329
329
|
*
|
|
330
330
|
* Each attempt owns a separate reservation. Compatibility recovery runs
|
|
331
331
|
* between those reservations, and a retry is admitted only when the exact
|
|
332
|
-
* first-attempt context remains active.
|
|
333
|
-
*
|
|
332
|
+
* first-attempt context remains active. Each attempt uses a detached settings
|
|
333
|
+
* snapshot, and a compatibility retry captures its snapshot after recovery.
|
|
334
334
|
*/
|
|
335
335
|
async performDirectionalReplication(direction, showingNotice, admission = DIRECTIONAL_REPLICATION_ADMISSION.ORDINARY) {
|
|
336
336
|
if (direction === DIRECTIONAL_REPLICATION.UPLOAD && !await this.onBeforeReplicate(showingNotice)) {
|
|
337
337
|
this._log(this.context.translate("Replicator.Message.SomeModuleFailed"), LOG_LEVEL_NOTICE);
|
|
338
338
|
return false;
|
|
339
339
|
}
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
340
|
+
const captureSetting = () => {
|
|
341
|
+
const detachedSetting = asCopy(this.settingService.currentSettings());
|
|
342
|
+
if (admission === DIRECTIONAL_REPLICATION_ADMISSION.EXPLICIT_REBUILD) {
|
|
343
|
+
detachedSetting.versionUpFlash = "";
|
|
344
|
+
}
|
|
345
|
+
return Object.freeze(detachedSetting);
|
|
346
|
+
};
|
|
347
|
+
const setting = captureSetting();
|
|
345
348
|
let expectedContext;
|
|
346
|
-
const run = async () => {
|
|
349
|
+
const run = async (attemptSetting) => {
|
|
347
350
|
const admitted = await this.replicatorService.runWithActiveReplicatorContext((context) => {
|
|
348
351
|
if (expectedContext && context !== expectedContext) {
|
|
349
352
|
return replicationBlocked("not-ready");
|
|
350
353
|
}
|
|
351
354
|
expectedContext ??= context;
|
|
352
|
-
return runDirectionalReplication(context,
|
|
355
|
+
return runDirectionalReplication(context, attemptSetting, direction, showingNotice);
|
|
353
356
|
});
|
|
354
357
|
if (admitted) return admitted;
|
|
355
358
|
this._log(`Active replicator not found during directional ${direction}`, LOG_LEVEL_NOTICE);
|
|
356
359
|
return replicationBlocked("no-active-replicator");
|
|
357
360
|
};
|
|
358
|
-
const outcome = await run();
|
|
361
|
+
const outcome = await run(setting);
|
|
359
362
|
if (isReplicationCompleted(outcome)) return true;
|
|
360
363
|
if (outcome.status !== "failed") return false;
|
|
361
364
|
const failedContext = expectedContext;
|
|
362
365
|
if (!failedContext) return false;
|
|
363
366
|
const failure = Object.freeze({ context: failedContext, setting, outcome });
|
|
364
367
|
const checkResult = await this.checkConnectionFailure(failure);
|
|
365
|
-
return checkResult === "CHECKAGAIN" && isReplicationCompleted(await run());
|
|
368
|
+
return checkResult === "CHECKAGAIN" && isReplicationCompleted(await run(captureSetting()));
|
|
366
369
|
}
|
|
367
370
|
async replicateAllToRemote(showingNotice = false) {
|
|
368
371
|
if (!this.appLifecycleService.isReady()) return false;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ObsidianLiveSyncSettings, RemoteDBSettings, RemotePreferredTweakResult, TweakValues } from "../../common/types.js";
|
|
1
|
+
import type { ObsidianLiveSyncSettings, RemoteDBSettings, RemotePreferredTweakResult, TweakAssessment, TweakValues } from "../../common/types.js";
|
|
2
2
|
import type { ITweakValueService } from "./IService.js";
|
|
3
3
|
import { ServiceBase, type ServiceContext } from "./ServiceBase.js";
|
|
4
4
|
/**
|
|
@@ -13,14 +13,16 @@ export declare abstract class TweakValueService<T extends ServiceContext = Servi
|
|
|
13
13
|
/**
|
|
14
14
|
* Check and ask the user to resolve any mismatched tweak values.
|
|
15
15
|
* @param preferred The preferred tweak values to check against.
|
|
16
|
+
* @param assessment The exact compatibility assessment from the calling operation, when available.
|
|
16
17
|
*/
|
|
17
|
-
abstract checkAndAskResolvingMismatched(preferred: Partial<TweakValues
|
|
18
|
+
abstract checkAndAskResolvingMismatched(preferred: Partial<TweakValues>, assessment?: TweakAssessment): Promise<[TweakValues | boolean, boolean]>;
|
|
18
19
|
/**
|
|
19
20
|
* Ask the user to resolve any mismatched tweak values.
|
|
20
21
|
* @param preferredSource The preferred tweak values to resolve against.
|
|
21
22
|
* @param updatePreferredRemote Optional exact-context writer supplied by the failed operation.
|
|
23
|
+
* @param assessment The exact compatibility assessment from the failed operation, when available.
|
|
22
24
|
*/
|
|
23
|
-
abstract askResolvingMismatched(preferredSource: TweakValues, updatePreferredRemote?: (setting: ObsidianLiveSyncSettings) => Promise<boolean
|
|
25
|
+
abstract askResolvingMismatched(preferredSource: TweakValues, updatePreferredRemote?: (setting: ObsidianLiveSyncSettings) => Promise<boolean>, assessment?: TweakAssessment): Promise<"OK" | "CHECKAGAIN" | "IGNORE">;
|
|
24
26
|
/**
|
|
25
27
|
* Check and ask the user to use the remote configuration.
|
|
26
28
|
* @param settings The remote database settings to connect.
|
|
@@ -2,14 +2,14 @@ import type { ServiceContext } from "../../base/ServiceBase.js";
|
|
|
2
2
|
import { TweakValueService } from "../../base/TweakValueService.js";
|
|
3
3
|
export declare class InjectableTweakValueService<T extends ServiceContext> extends TweakValueService<T> {
|
|
4
4
|
fetchRemotePreferred: import("../../lib/HandlerUtils.js").HandlerFunction<(trialSetting: import("../../../settings.js").RemoteDBSettings) => Promise<import("../../../common/types.js").RemotePreferredTweakResult>, unknown>;
|
|
5
|
-
checkAndAskResolvingMismatched: import("../../lib/HandlerUtils.js").HandlerFunction<(preferred: Partial<import("../../../
|
|
6
|
-
askResolvingMismatched: import("../../lib/HandlerUtils.js").HandlerFunction<(preferredSource: import("../../../
|
|
5
|
+
checkAndAskResolvingMismatched: import("../../lib/HandlerUtils.js").HandlerFunction<(preferred: Partial<import("../../../settings.js").TweakValues>, assessment?: import("../../../settings.js").TweakAssessment) => Promise<[import("../../../settings.js").TweakValues | boolean, boolean]>, unknown>;
|
|
6
|
+
askResolvingMismatched: import("../../lib/HandlerUtils.js").HandlerFunction<(preferredSource: import("../../../settings.js").TweakValues, updatePreferredRemote?: (setting: import("../../../settings.js").ObsidianLiveSyncSettings) => Promise<boolean>, assessment?: import("../../../settings.js").TweakAssessment) => Promise<"OK" | "CHECKAGAIN" | "IGNORE">, unknown>;
|
|
7
7
|
checkAndAskUseRemoteConfiguration: import("../../lib/HandlerUtils.js").HandlerFunction<(settings: import("../../../settings.js").RemoteDBSettings) => Promise<{
|
|
8
|
-
result: false | import("../../../
|
|
8
|
+
result: false | import("../../../settings.js").TweakValues;
|
|
9
9
|
requireFetch: boolean;
|
|
10
10
|
}>, unknown>;
|
|
11
|
-
askUseRemoteConfiguration: import("../../lib/HandlerUtils.js").HandlerFunction<(trialSetting: import("../../../settings.js").RemoteDBSettings, preferred: import("../../../
|
|
12
|
-
result: false | import("../../../
|
|
11
|
+
askUseRemoteConfiguration: import("../../lib/HandlerUtils.js").HandlerFunction<(trialSetting: import("../../../settings.js").RemoteDBSettings, preferred: import("../../../settings.js").TweakValues) => Promise<{
|
|
12
|
+
result: false | import("../../../settings.js").TweakValues;
|
|
13
13
|
requireFetch: boolean;
|
|
14
14
|
}>, unknown>;
|
|
15
15
|
}
|
package/dist/settings.d.ts
CHANGED
|
@@ -7,4 +7,7 @@ export { DEFAULT_SETTINGS, NEW_VAULT_SETTINGS, P2P_DEFAULT_SETTINGS, SETTINGS_SC
|
|
|
7
7
|
export { PREFERRED_BASE, PREFERRED_JOURNAL_SYNC, PREFERRED_SETTING_CLOUDANT, PREFERRED_SETTING_SELF_HOSTED, } from "./common/models/setting.const.preferred.js";
|
|
8
8
|
export { CURRENT_SETTING_VERSION } from "./common/models/setting.const.js";
|
|
9
9
|
export { prepareSettingsForLoad, SettingsMigrationReviewCodes, type PreparedSettings, type SettingsMigrationReviewCode, type SettingsMigrationReviewReason, type SettingsMigrationState, } from "./common/models/setting.lifecycle.js";
|
|
10
|
+
export { assessTweakCompatibility } from "./common/models/tweak.compatibility.js";
|
|
11
|
+
export type { TweakAssessment, TweakAssessmentEntry, TweakAssessmentRelation, TweakAssessmentValue, TweakTransition, TweakTransitionReason, } from "./common/models/tweak.compatibility.js";
|
|
12
|
+
export type { TweakReconstruction, TweakValues } from "./common/models/tweak.definition.js";
|
|
10
13
|
export type { ObsidianLiveSyncSettings, RemoteDBSettings, RemoteTypeSettings, } from "./common/models/setting.type.js";
|
package/dist/settings.js
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
prepareSettingsForLoad,
|
|
17
17
|
SettingsMigrationReviewCodes
|
|
18
18
|
} from "./common/models/setting.lifecycle.js";
|
|
19
|
+
import { assessTweakCompatibility } from "./common/models/tweak.compatibility.js";
|
|
19
20
|
export {
|
|
20
21
|
CURRENT_SETTING_VERSION,
|
|
21
22
|
DEFAULT_SETTINGS,
|
|
@@ -27,6 +28,7 @@ export {
|
|
|
27
28
|
PREFERRED_SETTING_SELF_HOSTED,
|
|
28
29
|
SETTINGS_SCHEMA_DEFAULTS,
|
|
29
30
|
SettingsMigrationReviewCodes,
|
|
31
|
+
assessTweakCompatibility,
|
|
30
32
|
createNewVaultSettings,
|
|
31
33
|
prepareSettingsForLoad
|
|
32
34
|
};
|