@vrtmrz/livesync-commonlib 0.1.22 → 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.
Files changed (35) hide show
  1. package/dist/common/models/fileaccess.type.d.ts +2 -0
  2. package/dist/common/models/tweak.compatibility.d.ts +34 -0
  3. package/dist/common/models/tweak.compatibility.js +110 -0
  4. package/dist/common/models/tweak.definition.d.ts +105 -19
  5. package/dist/common/models/tweak.definition.js +62 -29
  6. package/dist/common/types.d.ts +3 -0
  7. package/dist/managers/StorageEventManager.js +1 -0
  8. package/dist/pouchdb/LiveSyncDBFunctions.d.ts +3 -3
  9. package/dist/pouchdb/LiveSyncDBFunctions.js +9 -15
  10. package/dist/replication/CentralCompatibility.d.ts +4 -2
  11. package/dist/replication/CentralCompatibility.js +5 -3
  12. package/dist/replication/couchdb/LiveSyncReplicator.js +10 -2
  13. package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +2 -2
  14. package/dist/replication/journal/LiveSyncJournalReplicator.js +18 -4
  15. package/dist/replication/trystero/TrysteroReplicator.js +3 -4
  16. package/dist/serviceFeatures/targetFilter.d.ts +2 -1
  17. package/dist/serviceFeatures/targetFilter.js +2 -1
  18. package/dist/serviceModules/ServiceFileHandlerBase.d.ts +1 -0
  19. package/dist/serviceModules/ServiceFileHandlerBase.js +47 -2
  20. package/dist/services/base/IService.d.ts +8 -4
  21. package/dist/services/base/ReplicationService.d.ts +2 -2
  22. package/dist/services/base/ReplicationService.js +14 -11
  23. package/dist/services/base/TweakValueService.d.ts +5 -3
  24. package/dist/services/base/VaultService.d.ts +4 -2
  25. package/dist/services/base/VaultService.js +1 -1
  26. package/dist/services/implements/injectable/InjectableTweakValueService.d.ts +5 -5
  27. package/dist/settings.d.ts +3 -0
  28. package/dist/settings.js +2 -0
  29. package/dist/worker/bgWorker.js +1 -1
  30. package/docs/development.md +2 -0
  31. package/docs/offline-scanner.md +159 -0
  32. package/docs/proven-in-use.md +6 -0
  33. package/docs/settings-lifecycle.md +20 -1
  34. package/docs/storage-events-and-reflection.md +107 -0
  35. package/package.json +1 -1
@@ -69,7 +69,7 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
69
69
  }
70
70
  return this._client;
71
71
  }
72
- async ensureBucketIsCompatible(deviceNodeID, currentVersionRange2, setting = this.currentSettings, client = this.setupJournalSyncClient(setting)) {
72
+ async ensureBucketIsCompatible(deviceNodeID, currentVersionRange2, setting = this.currentSettings, client = this.setupJournalSyncClient(setting), recordTweakAssessment) {
73
73
  const milestoneResult = await client.downloadJsonWithResult(MILSTONE_DOCID);
74
74
  if (milestoneResult.status === JournalStorageReadStatuses.UNAVAILABLE) {
75
75
  throw milestoneResult.error;
@@ -93,7 +93,8 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
93
93
  if (!await client.uploadJson(MILSTONE_DOCID, info)) {
94
94
  throw new Error("Could not upload remote milestone");
95
95
  }
96
- }
96
+ },
97
+ recordTweakAssessment
97
98
  );
98
99
  }
99
100
  constructor(env) {
@@ -259,7 +260,16 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
259
260
  this.remoteLockedAndDeviceNotAccepted = false;
260
261
  this.tweakSettingsMismatched = false;
261
262
  this.preferredTweakValue = void 0;
262
- const ensure = await this.ensureBucketIsCompatible(this.nodeid, currentVersionRange, setting, client);
263
+ let tweakAssessment;
264
+ const ensure = await this.ensureBucketIsCompatible(
265
+ this.nodeid,
266
+ currentVersionRange,
267
+ setting,
268
+ client,
269
+ (assessment) => {
270
+ tweakAssessment = assessment;
271
+ }
272
+ );
263
273
  if (ensure == "INCOMPATIBLE") {
264
274
  recordCompatibilityDecision?.(
265
275
  centralCompatibilityRejected(CENTRAL_COMPATIBILITY_REJECTION_REASONS.INCOMPATIBLE_VERSION)
@@ -301,7 +311,11 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
301
311
  } else if (ensure == "OK") {
302
312
  } else if (ensure[0] == "MISMATCHED") {
303
313
  recordCompatibilityDecision?.(
304
- centralCompatibilityRejected(CENTRAL_COMPATIBILITY_REJECTION_REASONS.TWEAK_MISMATCH, ensure[1])
314
+ centralCompatibilityRejected(
315
+ CENTRAL_COMPATIBILITY_REJECTION_REASONS.TWEAK_MISMATCH,
316
+ ensure[1],
317
+ tweakAssessment
318
+ )
305
319
  );
306
320
  Logger(this.translate("liveSyncReplicator.mismatchedTweakDetected"), LOG_LEVEL_NOTICE);
307
321
  this.tweakSettingsMismatched = true;
@@ -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,7 @@
1
1
  import { type UXFileInfoStub } from "../common/types.js";
2
2
  import { type LogFunction } from "../services/lib/logUtils.js";
3
3
  import type { NecessaryServices } from "../interfaces/ServiceModule.js";
4
+ import type { IVaultService } from "../services/base/IService.js";
4
5
  /**
5
6
  * This is a simple handler that accepts all files.
6
7
  */
@@ -8,7 +9,7 @@ export declare function isAcceptedAlwaysFactory(host: NecessaryServices<never, n
8
9
  /**
9
10
  * Check if a file is accepted based on filename duplication in the vault.
10
11
  */
11
- export declare function isAcceptedInFilenameDuplicationFactory(host: NecessaryServices<"vault" | "fileProcessing", "storageAccess">, log: LogFunction): (file: string | UXFileInfoStub) => Promise<boolean>;
12
+ export declare function isAcceptedInFilenameDuplicationFactory(host: NecessaryServices<"vault" | "fileProcessing", "storageAccess">, log: LogFunction): (file: string | UXFileInfoStub, options?: Parameters<IVaultService["isTargetFile"]>[1]) => Promise<boolean>;
12
13
  /**
13
14
  * Check if a file is accepted by the local database (e.g., not rejected by the local DB's target file check).
14
15
  * Local database responsible for non-internal files, syncOnlyRegEx, syncIgnoreRegEx
@@ -35,7 +35,8 @@ function isAcceptedInFilenameDuplicationFactory(host, log) {
35
35
  return true;
36
36
  }
37
37
  });
38
- return async function isAcceptedInFilenameDuplication(file) {
38
+ return async function isAcceptedInFilenameDuplication(file, options) {
39
+ if (options?.skipCaseCollisionCheck) return true;
39
40
  const fileCountMap = (await fileCountMapComputed.update(host.services.fileProcessing.totalStorageFileEventCount)).value;
40
41
  const filepath = getStoragePathFromUXFileInfo(file);
41
42
  const lc = filepath.toLowerCase();
@@ -68,6 +68,7 @@ export declare abstract class ServiceFileHandlerBase extends ServiceModuleBase<S
68
68
  private applyDatabaseEntryToStorage;
69
69
  private preserveUnsyncedStorageAsConflict;
70
70
  private _anyHandlerProcessesFileEvent;
71
+ private canRecordStorageDeletion;
71
72
  private serializedByFileEventPaths;
72
73
  /**
73
74
  * Revalidate a persisted storage operation against the current storage state.
@@ -657,13 +657,15 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
657
657
  this._log(`Rename event for ${path} has no source path`, LOG_LEVEL_VERBOSE);
658
658
  return false;
659
659
  }
660
- const eventPaths = type === "RENAME" ? [path, eventItem.oldPath] : [path];
660
+ const relatedPath = type === "RENAME" ? eventItem.oldPath : type === "DELETE" ? eventItem.renameTarget : void 0;
661
+ const eventPaths = relatedPath === void 0 ? [path] : [path, relatedPath];
661
662
  return await this.serializedByFileEventPaths(eventPaths, async () => {
662
663
  switch (type) {
663
664
  case "CREATE":
664
665
  case "CHANGED":
665
666
  return await this.storeFileToDB(item.args.file);
666
667
  case "DELETE":
668
+ if (!await this.canRecordStorageDeletion(item)) return true;
667
669
  return await this.deleteFileFromDB(item.args.file);
668
670
  case "RENAME":
669
671
  return await this.renameFileInDB(
@@ -678,6 +680,45 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
678
680
  }
679
681
  });
680
682
  }
683
+ async canRecordStorageDeletion(item) {
684
+ const path = item.args.file.path;
685
+ try {
686
+ const current = await this.storage.getStub(path);
687
+ let excludedRenameTarget = null;
688
+ if (item.args.renameTarget !== void 0) {
689
+ const targetPath = item.args.renameTarget;
690
+ const targetItem = await this.storage.getStub(targetPath);
691
+ const target = this.getExactCurrentFile(targetItem, targetPath);
692
+ if (targetItem !== null && !target) {
693
+ this._log(`Preserved ${path}: the rename target no longer resolves to ${targetPath}`, LOG_LEVEL_NOTICE);
694
+ return false;
695
+ }
696
+ const targetIsSelected = !shouldBeIgnored(targetPath) && await this.vault.isTargetFile(targetPath, { skipCaseCollisionCheck: true }) && !(target && this.vault.isFileSizeTooLarge(target.stat.size));
697
+ if (targetIsSelected) {
698
+ this._log(`Preserved ${path}: rename target ${targetPath} is still selected`, LOG_LEVEL_NOTICE);
699
+ return false;
700
+ }
701
+ excludedRenameTarget = target;
702
+ }
703
+ if (current === null) return true;
704
+ if (!isFolderInfo(current)) {
705
+ const [currentId, deletedId] = await Promise.all([
706
+ this.path.path2id(current.path),
707
+ this.path.path2id(path)
708
+ ]);
709
+ if (currentId !== deletedId) return true;
710
+ if (excludedRenameTarget && this.storage.normalisePath(current.path) !== this.storage.normalisePath(path) && this.storage.normalisePath(current.path) === this.storage.normalisePath(excludedRenameTarget.path)) {
711
+ return true;
712
+ }
713
+ }
714
+ this._log(`Preserved ${path}: a current storage item contradicts its deletion event`, LOG_LEVEL_NOTICE);
715
+ return false;
716
+ } catch (ex) {
717
+ this._log(`Could not validate deletion of ${path}; preserved the database entry`, LOG_LEVEL_NOTICE);
718
+ this._log(ex, LOG_LEVEL_VERBOSE);
719
+ return false;
720
+ }
721
+ }
681
722
  async serializedByFileEventPaths(eventPaths, callback) {
682
723
  const documentIds = await Promise.all(eventPaths.map((eventPath) => this.path.path2id(eventPath)));
683
724
  const lockKeys = [...new Set(documentIds)].sort().map((documentId) => `processFileEvent-${documentId}`);
@@ -702,7 +743,8 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
702
743
  this._log(`Rename event for ${path} has no source path`, LOG_LEVEL_VERBOSE);
703
744
  return true;
704
745
  }
705
- const eventPaths = type === "RENAME" ? [path, eventItem.oldPath] : [path];
746
+ const relatedPath = type === "RENAME" ? eventItem.oldPath : type === "DELETE" ? eventItem.renameTarget : void 0;
747
+ const eventPaths = relatedPath === void 0 ? [path] : [path, relatedPath];
706
748
  return await this.serializedByFileEventPaths(eventPaths, async (isSameDocument) => {
707
749
  let action;
708
750
  try {
@@ -732,6 +774,9 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
732
774
  return current && await this.isCurrentFileSelected(current) ? { kind: "store", file: current } : { kind: "none" };
733
775
  }
734
776
  case "DELETE": {
777
+ if (item.args.renameTarget !== void 0) {
778
+ return await this.canRecordStorageDeletion(item) && await this.canApplyRestoredDeletion(path) ? { kind: "delete", path } : { kind: "none" };
779
+ }
735
780
  const current = await this.storage.getStub(path);
736
781
  return current === null && await this.canApplyRestoredDeletion(path) ? { kind: "delete", path } : { kind: "none" };
737
782
  }
@@ -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;
@@ -351,7 +351,11 @@ export interface IVaultService {
351
351
  getVaultName(): string;
352
352
  scanVault(showingNotice?: boolean, ignoreSuspending?: boolean, continueOnFileFailure?: boolean): Promise<VaultScanResult>;
353
353
  isIgnoredByIgnoreFile(file: string | UXFileInfoStub): Promise<boolean>;
354
- isTargetFile(file: string | UXFileInfoStub): Promise<boolean>;
354
+ isTargetFile(file: string | UXFileInfoStub,
355
+ /** Inspect selection policy without treating a filename collision as an exclusion. */
356
+ options?: {
357
+ skipCaseCollisionCheck?: boolean;
358
+ }): Promise<boolean>;
355
359
  isTargetFileInExtra(file: string | UXFileInfoStub): Promise<boolean>;
356
360
  isFileSizeTooLarge(size: number): boolean;
357
361
  getActiveFilePath(): FilePath | undefined;
@@ -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.
@@ -35,9 +35,11 @@ export declare abstract class VaultService<T extends ServiceContext = ServiceCon
35
35
  /**
36
36
  * Check if a file is a target file for synchronisation.
37
37
  * @param file The file path or file info stub to check.
38
- * @param keepFileCheckList Whether to keep the file in the check list.
38
+ * @param options Optional selection inspection without filename collision rejection.
39
39
  */
40
- readonly isTargetFile: import("../lib/HandlerUtils.js").BooleanMultipleHandlerFunction<(file: string | import("../../common/types.js").UXFileInfoStub) => Promise<boolean>>;
40
+ readonly isTargetFile: import("../lib/HandlerUtils.js").BooleanMultipleHandlerFunction<(file: string | import("../../common/types.js").UXFileInfoStub, options?: {
41
+ skipCaseCollisionCheck?: boolean;
42
+ }) => Promise<boolean>>;
41
43
  /**
42
44
  * Check if a file is a target file for some extra feature
43
45
  */
@@ -37,7 +37,7 @@ class VaultService extends ServiceBase {
37
37
  /**
38
38
  * Check if a file is a target file for synchronisation.
39
39
  * @param file The file path or file info stub to check.
40
- * @param keepFileCheckList Whether to keep the file in the check list.
40
+ * @param options Optional selection inspection without filename collision rejection.
41
41
  */
42
42
  isTargetFile = handlers().bailFirstFailure("isTargetFile");
43
43
  /**
@@ -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
  };