@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.
@@ -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>): Promise<[TweakValues | boolean, boolean]>;
339
- askResolvingMismatched(preferredSource: TweakValues, updatePreferredRemote?: (setting: ObsidianLiveSyncSettings) => Promise<boolean>): Promise<"OK" | "CHECKAGAIN" | "IGNORE">;
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. Both attempts use one detached
121
- * settings snapshot.
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. Both attempts use one detached
333
- * settings snapshot.
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 detachedSetting = asCopy(this.settingService.currentSettings());
341
- if (admission === DIRECTIONAL_REPLICATION_ADMISSION.EXPLICIT_REBUILD) {
342
- detachedSetting.versionUpFlash = "";
343
- }
344
- const setting = Object.freeze(detachedSetting);
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, setting, direction, showingNotice);
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>): Promise<[TweakValues | boolean, boolean]>;
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>): Promise<"OK" | "CHECKAGAIN" | "IGNORE">;
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("../../../common/types.js").TweakValues>) => Promise<[import("../../../common/types.js").TweakValues | boolean, boolean]>, unknown>;
6
- askResolvingMismatched: import("../../lib/HandlerUtils.js").HandlerFunction<(preferredSource: import("../../../common/types.js").TweakValues, updatePreferredRemote?: (setting: import("../../../settings.js").ObsidianLiveSyncSettings) => Promise<boolean>) => Promise<"OK" | "CHECKAGAIN" | "IGNORE">, unknown>;
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("../../../common/types.js").TweakValues;
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("../../../common/types.js").TweakValues) => Promise<{
12
- result: false | import("../../../common/types.js").TweakValues;
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
  }
@@ -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
  };