@vrtmrz/livesync-commonlib 0.1.0-rc.1 → 0.1.0-rc.2

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 (59) hide show
  1. package/README.md +87 -19
  2. package/dist/common/i18n.js +7 -7099
  3. package/dist/common/messages/combinedMessages.prod.d.ts +3 -0
  4. package/dist/common/messages/combinedMessages.prod.js +3 -0
  5. package/dist/common/messages/def.d.ts +1 -0
  6. package/dist/common/messagesJson/en.json +1 -0
  7. package/dist/common/models/setting.const.defaults.d.ts +19 -0
  8. package/dist/common/models/setting.const.defaults.js +20 -2
  9. package/dist/common/models/setting.lifecycle.d.ts +31 -0
  10. package/dist/common/models/setting.lifecycle.js +109 -0
  11. package/dist/common/types.d.ts +4 -2
  12. package/dist/common/types.js +16 -1
  13. package/dist/interfaces/Confirm.d.ts +8 -1
  14. package/dist/interfaces/DatabaseRebuilder.d.ts +12 -2
  15. package/dist/remoteConfigurations.d.ts +9 -0
  16. package/dist/remoteConfigurations.js +14 -0
  17. package/dist/replication/trystero/LiveSyncTrysteroReplicator.d.ts +4 -1
  18. package/dist/replication/trystero/LiveSyncTrysteroReplicator.js +41 -26
  19. package/dist/replication/trystero/P2PReplicatorCore.d.ts +2 -6
  20. package/dist/replication/trystero/P2PReplicatorCore.js +16 -86
  21. package/dist/replication/trystero/TrysteroReplicator.d.ts +2 -1
  22. package/dist/replication/trystero/TrysteroReplicator.js +10 -4
  23. package/dist/replication/trystero/TrysteroReplicatorP2PServer.d.ts +4 -5
  24. package/dist/replication/trystero/TrysteroReplicatorP2PServer.js +22 -30
  25. package/dist/replication/trystero/UseP2PReplicatorResult.d.ts +10 -2
  26. package/dist/replication/trystero/addP2PEventHandlers.d.ts +4 -2
  27. package/dist/replication/trystero/addP2PEventHandlers.js +8 -6
  28. package/dist/replication/trystero/types.d.ts +2 -0
  29. package/dist/replication/trystero/useP2PReplicatorCommands.d.ts +1 -1
  30. package/dist/replication/trystero/useP2PReplicatorCommands.js +3 -1
  31. package/dist/replication/trystero/useP2PReplicatorFeature.js +1 -1
  32. package/dist/rpc/RpcRoom.d.ts +1 -1
  33. package/dist/rpc/RpcRoom.js +22 -16
  34. package/dist/rpc/transports/TrysteroTransport.d.ts +2 -0
  35. package/dist/rpc/transports/TrysteroTransport.js +24 -17
  36. package/dist/rpc/transports/trysteroRoomEvents.d.ts +14 -0
  37. package/dist/rpc/transports/trysteroRoomEvents.js +34 -0
  38. package/dist/serviceFeatures/remoteConfig.d.ts +37 -1
  39. package/dist/serviceFeatures/remoteConfig.js +90 -0
  40. package/dist/serviceModules/Rebuilder.d.ts +3 -2
  41. package/dist/serviceModules/Rebuilder.js +21 -9
  42. package/dist/services/base/IService.d.ts +12 -1
  43. package/dist/services/base/ReplicationService.d.ts +3 -1
  44. package/dist/services/base/ReplicationService.js +6 -1
  45. package/dist/services/base/SettingService.d.ts +14 -1
  46. package/dist/services/base/SettingService.js +35 -12
  47. package/dist/services/implements/headless/HeadlessAPIService.d.ts +1 -1
  48. package/dist/services/implements/headless/HeadlessAPIService.js +1 -1
  49. package/dist/services/implements/injectable/InjectableTweakValueService.d.ts +3 -3
  50. package/dist/settings.d.ts +10 -0
  51. package/dist/settings.js +32 -0
  52. package/dist/worker/bgWorker.js +1 -1
  53. package/docs/development.md +75 -1
  54. package/docs/p2p-transport-lifecycle.md +52 -0
  55. package/docs/proven-in-use.md +55 -0
  56. package/docs/releasing.md +9 -5
  57. package/docs/remote-configurations.md +63 -0
  58. package/docs/settings-lifecycle.md +60 -0
  59. package/package.json +12 -2
@@ -8631,6 +8631,9 @@ export declare const allMessages: {
8631
8631
  readonly def: "Welcome to Self-hosted LiveSync";
8632
8632
  readonly zh: "欢迎使用 Self-hosted LiveSync";
8633
8633
  };
8634
+ readonly "Ui.SetupWizard.Invitation.Start": {
8635
+ readonly def: "Start setup";
8636
+ };
8634
8637
  readonly "Ui.SetupWizard.OutroAskUserMode.CompatibleOption": {
8635
8638
  readonly def: "The remote is already set up, and the configuration is compatible (or became compatible through this operation).";
8636
8639
  readonly zh: "远程端已配置完成,且当前配置兼容(或已通过本次操作变为兼容)。";
@@ -8635,6 +8635,9 @@ Note : le QR code n'est pas chiffr\xE9, soyez prudent en l'affichant.
8635
8635
  def: "Welcome to Self-hosted LiveSync",
8636
8636
  zh: "\u6B22\u8FCE\u4F7F\u7528 Self-hosted LiveSync"
8637
8637
  },
8638
+ "Ui.SetupWizard.Invitation.Start": {
8639
+ def: "Start setup"
8640
+ },
8638
8641
  "Ui.SetupWizard.OutroAskUserMode.CompatibleOption": {
8639
8642
  def: "The remote is already set up, and the configuration is compatible (or became compatible through this operation).",
8640
8643
  zh: "\u8FDC\u7A0B\u7AEF\u5DF2\u914D\u7F6E\u5B8C\u6210\uFF0C\u4E14\u5F53\u524D\u914D\u7F6E\u517C\u5BB9\uFF08\u6216\u5DF2\u901A\u8FC7\u672C\u6B21\u64CD\u4F5C\u53D8\u4E3A\u517C\u5BB9\uFF09\u3002"
@@ -1040,6 +1040,7 @@ export declare const PartialMessages: {
1040
1040
  "Ui.SetupWizard.Intro.ProceedNew": string;
1041
1041
  "Ui.SetupWizard.Intro.Question": string;
1042
1042
  "Ui.SetupWizard.Intro.Title": string;
1043
+ "Ui.SetupWizard.Invitation.Start": string;
1043
1044
  "Ui.SetupWizard.OutroAskUserMode.CompatibleOption": string;
1044
1045
  "Ui.SetupWizard.OutroAskUserMode.CompatibleOptionDesc": string;
1045
1046
  "Ui.SetupWizard.OutroAskUserMode.ExistingOption": string;
@@ -1039,6 +1039,7 @@
1039
1039
  "Ui.SetupWizard.Intro.ProceedNew": "Yes, I want to set up a new synchronisation",
1040
1040
  "Ui.SetupWizard.Intro.Question": "First, please select the option that best describes your current situation.",
1041
1041
  "Ui.SetupWizard.Intro.Title": "Welcome to Self-hosted LiveSync",
1042
+ "Ui.SetupWizard.Invitation.Start": "Start setup",
1042
1043
  "Ui.SetupWizard.OutroAskUserMode.CompatibleOption": "The remote is already set up, and the configuration is compatible (or became compatible through this operation).",
1043
1044
  "Ui.SetupWizard.OutroAskUserMode.CompatibleOptionDesc": "Unless you are certain, selecting this option is risky. It assumes the server configuration is compatible with this device. If that is not the case, data loss may occur. Please make sure you understand the consequences.",
1044
1045
  "Ui.SetupWizard.OutroAskUserMode.ExistingOption": "My remote server is already set up. I want to join this device.",
@@ -1,3 +1,22 @@
1
1
  import { type ObsidianLiveSyncSettings, type P2PSyncSetting } from "./setting.type.js";
2
2
  export declare const P2P_DEFAULT_SETTINGS: P2PSyncSetting;
3
+ /**
4
+ * Conservative fallback values used to complete stored settings.
5
+ *
6
+ * Keep these values compatible with existing installations. Defaults intended
7
+ * only for a newly created Vault belong in {@link NEW_VAULT_SETTINGS}.
8
+ */
9
+ export declare const SETTINGS_SCHEMA_DEFAULTS: ObsidianLiveSyncSettings;
10
+ /**
11
+ * Initial values for a Vault which has never stored Self-hosted LiveSync settings.
12
+ *
13
+ * This object is deliberately separate from schema fallbacks so that future
14
+ * releases can improve new-user defaults without changing existing choices.
15
+ */
16
+ export declare const NEW_VAULT_SETTINGS: ObsidianLiveSyncSettings;
17
+ /** Create an independently mutable copy of the current new-Vault settings. */
18
+ export declare function createNewVaultSettings(): ObsidianLiveSyncSettings;
19
+ /**
20
+ * Compatibility name for the conservative setting-schema fallbacks.
21
+ */
3
22
  export declare const DEFAULT_SETTINGS: ObsidianLiveSyncSettings;
@@ -1,4 +1,5 @@
1
1
  import { ChunkAlgorithms, CURRENT_SETTING_VERSION, E2EEAlgorithms, REMOTE_COUCHDB } from "./setting.const.js";
2
+ import { PREFERRED_BASE } from "./setting.const.preferred.js";
2
3
  import { AutoAccepting } from "./setting.type.js";
3
4
  const P2P_DEFAULT_SETTINGS = {
4
5
  P2P_Enabled: false,
@@ -22,7 +23,7 @@ const P2P_DEFAULT_SETTINGS = {
22
23
  P2P_turnCredential: "",
23
24
  P2P_useDiagRTC: false
24
25
  };
25
- const DEFAULT_SETTINGS = {
26
+ const SETTINGS_SCHEMA_DEFAULTS = {
26
27
  remoteType: REMOTE_COUCHDB,
27
28
  useCustomRequestHandler: false,
28
29
  couchDB_URI: "",
@@ -179,7 +180,24 @@ const DEFAULT_SETTINGS = {
179
180
  maxMTimeForReflectEvents: 0,
180
181
  tweakModified: void 0
181
182
  };
183
+ const NEW_VAULT_SETTINGS = {
184
+ ...SETTINGS_SCHEMA_DEFAULTS,
185
+ ...PREFERRED_BASE,
186
+ remoteConfigurations: {},
187
+ pluginSyncExtendedSetting: {}
188
+ };
189
+ function createNewVaultSettings() {
190
+ return {
191
+ ...NEW_VAULT_SETTINGS,
192
+ remoteConfigurations: {},
193
+ pluginSyncExtendedSetting: {}
194
+ };
195
+ }
196
+ const DEFAULT_SETTINGS = SETTINGS_SCHEMA_DEFAULTS;
182
197
  export {
183
198
  DEFAULT_SETTINGS,
184
- P2P_DEFAULT_SETTINGS
199
+ NEW_VAULT_SETTINGS,
200
+ P2P_DEFAULT_SETTINGS,
201
+ SETTINGS_SCHEMA_DEFAULTS,
202
+ createNewVaultSettings
185
203
  };
@@ -0,0 +1,31 @@
1
+ import type { ObsidianLiveSyncSettings } from "./setting.type.js";
2
+ export declare const SettingsMigrationReviewCodes: {
3
+ readonly LegacyUpdatePending: "legacy-update-review-pending";
4
+ readonly FutureSchema: "settings-schema-newer-than-runtime";
5
+ };
6
+ export type SettingsMigrationReviewCode = (typeof SettingsMigrationReviewCodes)[keyof typeof SettingsMigrationReviewCodes];
7
+ export interface SettingsMigrationReviewReason {
8
+ code: SettingsMigrationReviewCode;
9
+ fromVersion: number;
10
+ toVersion: number;
11
+ }
12
+ export interface SettingsMigrationState {
13
+ sourceVersion: number;
14
+ targetVersion: number;
15
+ isNewVault: boolean;
16
+ isFromFutureSchema: boolean;
17
+ changed: boolean;
18
+ requiresSyncReview: boolean;
19
+ reviewReasons: readonly SettingsMigrationReviewReason[];
20
+ }
21
+ export interface PreparedSettings extends SettingsMigrationState {
22
+ settings: ObsidianLiveSyncSettings;
23
+ }
24
+ /**
25
+ * Completes and migrates raw settings before services consume them.
26
+ *
27
+ * Stored settings use conservative schema fallbacks. A blank store uses the
28
+ * independently maintained new-Vault defaults. Explicit stored values always
29
+ * take precedence over both sets of defaults.
30
+ */
31
+ export declare function prepareSettingsForLoad(storedSettings: Partial<ObsidianLiveSyncSettings> | undefined): PreparedSettings;
@@ -0,0 +1,109 @@
1
+ import { createNewVaultSettings, SETTINGS_SCHEMA_DEFAULTS } from "./setting.const.defaults.js";
2
+ import {
3
+ CURRENT_SETTING_VERSION,
4
+ SETTING_VERSION_INITIAL,
5
+ SETTING_VERSION_SUPPORT_CASE_INSENSITIVE
6
+ } from "./setting.const.js";
7
+ const SettingsMigrationReviewCodes = {
8
+ LegacyUpdatePending: "legacy-update-review-pending",
9
+ FutureSchema: "settings-schema-newer-than-runtime"
10
+ };
11
+ const SETTINGS_MIGRATIONS = [
12
+ {
13
+ toVersion: SETTING_VERSION_SUPPORT_CASE_INSENSITIVE,
14
+ apply: (settings) => settings,
15
+ reviewReason: (settings, fromVersion) => settings.versionUpFlash ? {
16
+ code: SettingsMigrationReviewCodes.LegacyUpdatePending,
17
+ fromVersion,
18
+ toVersion: SETTING_VERSION_SUPPORT_CASE_INSENSITIVE
19
+ } : void 0
20
+ }
21
+ ];
22
+ function cloneSettings(source, fallbacks) {
23
+ return {
24
+ ...fallbacks,
25
+ ...source,
26
+ remoteConfigurations: Object.fromEntries(
27
+ Object.entries(source.remoteConfigurations ?? fallbacks.remoteConfigurations).map(([id, config]) => [
28
+ id,
29
+ { ...config }
30
+ ])
31
+ ),
32
+ pluginSyncExtendedSetting: {
33
+ ...fallbacks.pluginSyncExtendedSetting,
34
+ ...source.pluginSyncExtendedSetting
35
+ }
36
+ };
37
+ }
38
+ function storedSettingVersion(settings) {
39
+ const version = settings.settingVersion;
40
+ return typeof version === "number" && Number.isSafeInteger(version) && version >= SETTING_VERSION_INITIAL ? version : SETTING_VERSION_INITIAL;
41
+ }
42
+ function isBlankSettings(settings) {
43
+ return settings === void 0 || Object.keys(settings).length === 0;
44
+ }
45
+ function prepareSettingsForLoad(storedSettings) {
46
+ if (isBlankSettings(storedSettings)) {
47
+ return {
48
+ settings: createNewVaultSettings(),
49
+ sourceVersion: CURRENT_SETTING_VERSION,
50
+ targetVersion: CURRENT_SETTING_VERSION,
51
+ isNewVault: true,
52
+ isFromFutureSchema: false,
53
+ changed: false,
54
+ requiresSyncReview: false,
55
+ reviewReasons: []
56
+ };
57
+ }
58
+ const source = { ...storedSettings };
59
+ const sourceVersion = storedSettingVersion(source);
60
+ if (sourceVersion > CURRENT_SETTING_VERSION) {
61
+ const reviewReasons2 = [
62
+ {
63
+ code: SettingsMigrationReviewCodes.FutureSchema,
64
+ fromVersion: sourceVersion,
65
+ toVersion: CURRENT_SETTING_VERSION
66
+ }
67
+ ];
68
+ return {
69
+ settings: cloneSettings(source, SETTINGS_SCHEMA_DEFAULTS),
70
+ sourceVersion,
71
+ targetVersion: sourceVersion,
72
+ isNewVault: false,
73
+ isFromFutureSchema: true,
74
+ changed: false,
75
+ requiresSyncReview: true,
76
+ reviewReasons: reviewReasons2
77
+ };
78
+ }
79
+ let migrated = source;
80
+ let targetVersion = sourceVersion;
81
+ const reviewReasons = [];
82
+ for (const migration of SETTINGS_MIGRATIONS) {
83
+ if (targetVersion >= migration.toVersion) continue;
84
+ const reason = migration.reviewReason?.(migrated, targetVersion);
85
+ if (reason) reviewReasons.push(reason);
86
+ migrated = migration.apply(migrated);
87
+ targetVersion = migration.toVersion;
88
+ migrated.settingVersion = targetVersion;
89
+ }
90
+ if (targetVersion !== CURRENT_SETTING_VERSION) {
91
+ throw new Error(
92
+ `No setting migration reaches schema ${CURRENT_SETTING_VERSION} from schema ${sourceVersion}.`
93
+ );
94
+ }
95
+ return {
96
+ settings: cloneSettings(migrated, SETTINGS_SCHEMA_DEFAULTS),
97
+ sourceVersion,
98
+ targetVersion,
99
+ isNewVault: false,
100
+ isFromFutureSchema: false,
101
+ changed: sourceVersion !== targetVersion,
102
+ requiresSyncReview: reviewReasons.length > 0,
103
+ reviewReasons
104
+ };
105
+ }
106
+ export {
107
+ SettingsMigrationReviewCodes,
108
+ prepareSettingsForLoad
109
+ };
@@ -10,7 +10,8 @@ import { IDPrefixes, LEAF_WAIT_ONLY_REMOTE, LEAF_WAIT_TIMEOUT, LEAF_WAIT_TIMEOUT
10
10
  import { AutoAccepting, type BucketSyncSetting, type ChunkSplitterVersion, type ConfigPassphraseStore, type CouchDBConnection, type E2EEAlgorithm, type EncryptionSettings, type HashAlgorithm, type HasSettings, type LocalDBSettings, type ObsidianLiveSyncSettings, type P2PConnectionInfo, type P2PSyncSetting, type PluginSyncSettingEntry, type RemoteDBSettings, type RemoteType, type RemoteTypeSettings, type SYNC_MODE } from "./models/setting.type.js";
11
11
  import { ChunkAlgorithmNames, ChunkAlgorithms, CURRENT_SETTING_VERSION, E2EEAlgorithmNames, E2EEAlgorithms, HashAlgorithms, REMOTE_COUCHDB, REMOTE_MINIO, REMOTE_P2P, RemoteTypes, SETTING_VERSION_INITIAL, SETTING_VERSION_SUPPORT_CASE_INSENSITIVE, MODE_AUTOMATIC, MODE_PAUSED, MODE_SELECTIVE, MODE_SHINY } from "./models/setting.const.js";
12
12
  import { PREFERRED_BASE, PREFERRED_JOURNAL_SYNC, PREFERRED_SETTING_CLOUDANT, PREFERRED_SETTING_SELF_HOSTED } from "./models/setting.const.preferred.js";
13
- import { P2P_DEFAULT_SETTINGS, DEFAULT_SETTINGS } from "./models/setting.const.defaults.js";
13
+ import { P2P_DEFAULT_SETTINGS, DEFAULT_SETTINGS, NEW_VAULT_SETTINGS, SETTINGS_SCHEMA_DEFAULTS, createNewVaultSettings } from "./models/setting.const.defaults.js";
14
+ import { prepareSettingsForLoad, SettingsMigrationReviewCodes, type PreparedSettings, type SettingsMigrationReviewCode, type SettingsMigrationReviewReason, type SettingsMigrationState } from "./models/setting.lifecycle.js";
14
15
  import { KeyIndexOfSettings } from "./models/setting.const.qr.js";
15
16
  import type { DeviceInfo, EntryBody, EntryDoc, EntryDocResponse, EntryMilestoneInfo, EntryNodeInfo, NodeData, NodeKey } from "./models/db.definition.js";
16
17
  import { isMetaEntry } from "./models/db.definition.js";
@@ -42,7 +43,8 @@ export type { EncryptionSettings };
42
43
  export { HashAlgorithms, type HashAlgorithm, ChunkAlgorithmNames, ChunkAlgorithms, type ChunkSplitterVersion };
43
44
  export type { RemoteDBSettings };
44
45
  export type { ObsidianLiveSyncSettings };
45
- export { DEFAULT_SETTINGS };
46
+ export { DEFAULT_SETTINGS, NEW_VAULT_SETTINGS, SETTINGS_SCHEMA_DEFAULTS, createNewVaultSettings };
47
+ export { prepareSettingsForLoad, SettingsMigrationReviewCodes, type PreparedSettings, type SettingsMigrationReviewCode, type SettingsMigrationReviewReason, type SettingsMigrationState, };
46
48
  export { KeyIndexOfSettings };
47
49
  export { type HasSettings };
48
50
  export { PREFERRED_BASE, PREFERRED_SETTING_CLOUDANT, PREFERRED_SETTING_SELF_HOSTED, PREFERRED_JOURNAL_SYNC };
@@ -67,7 +67,17 @@ import {
67
67
  PREFERRED_SETTING_CLOUDANT,
68
68
  PREFERRED_SETTING_SELF_HOSTED
69
69
  } from "./models/setting.const.preferred.js";
70
- import { P2P_DEFAULT_SETTINGS, DEFAULT_SETTINGS } from "./models/setting.const.defaults.js";
70
+ import {
71
+ P2P_DEFAULT_SETTINGS,
72
+ DEFAULT_SETTINGS,
73
+ NEW_VAULT_SETTINGS,
74
+ SETTINGS_SCHEMA_DEFAULTS,
75
+ createNewVaultSettings
76
+ } from "./models/setting.const.defaults.js";
77
+ import {
78
+ prepareSettingsForLoad,
79
+ SettingsMigrationReviewCodes
80
+ } from "./models/setting.lifecycle.js";
71
81
  import { KeyIndexOfSettings } from "./models/setting.const.qr.js";
72
82
  import { isMetaEntry } from "./models/db.definition.js";
73
83
  import {
@@ -165,6 +175,7 @@ export {
165
175
  MODE_PAUSED,
166
176
  MODE_SELECTIVE,
167
177
  MODE_SHINY,
178
+ NEW_VAULT_SETTINGS,
168
179
  NODEINFO_DOCID,
169
180
  NOT_CONFLICTED,
170
181
  NoteTypes,
@@ -190,10 +201,12 @@ export {
190
201
  SALT_OF_ID,
191
202
  SALT_OF_PASSPHRASE,
192
203
  SEED_MURMURHASH,
204
+ SETTINGS_SCHEMA_DEFAULTS,
193
205
  SETTING_KEY_P2P_DEVICE_NAME,
194
206
  SETTING_VERSION_INITIAL,
195
207
  SETTING_VERSION_SUPPORT_CASE_INSENSITIVE,
196
208
  SYNCINFO_ID,
209
+ SettingsMigrationReviewCodes,
197
210
  SuffixDatabaseName,
198
211
  TIME_ARGUMENT_INFINITY,
199
212
  TweakValuesDefault,
@@ -207,6 +220,8 @@ export {
207
220
  configURIBase,
208
221
  configURIBaseQR,
209
222
  configurationNames,
223
+ createNewVaultSettings,
210
224
  isMetaEntry,
225
+ prepareSettingsForLoad,
211
226
  statusDisplay
212
227
  };
@@ -12,6 +12,13 @@ export interface Confirm {
12
12
  defaultAction: T[number];
13
13
  timeout?: number;
14
14
  }): Promise<T[number] | false>;
15
- askInPopup(key: string, dialogText: string, anchorCallback: (anchor: HTMLAnchorElement) => void): void;
15
+ /**
16
+ * Shows a non-blocking message containing one host-rendered action link.
17
+ *
18
+ * Hosts which can display transient UI should keep the message visible for
19
+ * `durationMs`. Headless hosts may report the message without presenting an
20
+ * interactive action.
21
+ */
22
+ askInPopup(key: string, dialogText: string, anchorCallback: (anchor: HTMLAnchorElement) => void, durationMs?: number): void;
16
23
  confirmWithMessage(title: string, contentMd: string, buttons: string[], defaultAction: (typeof buttons)[number], timeout?: number): Promise<(typeof buttons)[number] | false>;
17
24
  }
@@ -4,8 +4,18 @@ export interface Rebuilder {
4
4
  $rebuildEverything(): Promise<void>;
5
5
  $fetchLocal(makeLocalChunkBeforeSync?: boolean, preventMakeLocalFilesBeforeSync?: boolean): Promise<void>;
6
6
  $fetchLocalDBFast(autoResume: boolean): Promise<void>;
7
- scheduleRebuild(): Promise<void>;
8
- scheduleFetch(): Promise<void>;
7
+ /**
8
+ * Writes the Rebuild flag, suspends the current runtime, optionally prepares
9
+ * persisted state, and requests a restart, in that order.
10
+ *
11
+ * The preparation callback runs only after the flag exists. If it fails,
12
+ * the flag is removed, the current runtime is resumed, and the error is
13
+ * rethrown. A false result means that the flag could not be written and no
14
+ * restart was requested.
15
+ */
16
+ scheduleRebuild(prepareBeforeRestart?: () => Promise<void>): Promise<boolean>;
17
+ /** See {@link scheduleRebuild}; this variant writes the Fetch flag. */
18
+ scheduleFetch(prepareBeforeRestart?: () => Promise<void>): Promise<boolean>;
9
19
  /**
10
20
  * Declares the finish of the rebuild process and unlock remote, resume reflecting the changes.
11
21
  */
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Focused public primitives for storing and selecting multiple remote connection profiles.
3
+ *
4
+ * The compatibility fields remain available to existing hosts, but new profile-management code
5
+ * should use this entry point instead of importing the wider service feature module.
6
+ */
7
+ export { activateP2PRemoteConfiguration, activateRemoteConfiguration, createRemoteConfigurationId, suggestRemoteConfigurationName, upsertRemoteConfigurationInPlace, } from "./serviceFeatures/remoteConfig.js";
8
+ export type { SerializableRemoteConfigurationType, UpsertRemoteConfigurationOptions, } from "./serviceFeatures/remoteConfig.js";
9
+ export type { RemoteConfiguration } from "./common/models/setting.type.js";
@@ -0,0 +1,14 @@
1
+ import {
2
+ activateP2PRemoteConfiguration,
3
+ activateRemoteConfiguration,
4
+ createRemoteConfigurationId,
5
+ suggestRemoteConfigurationName,
6
+ upsertRemoteConfigurationInPlace
7
+ } from "./serviceFeatures/remoteConfig.js";
8
+ export {
9
+ activateP2PRemoteConfiguration,
10
+ activateRemoteConfiguration,
11
+ createRemoteConfigurationId,
12
+ suggestRemoteConfigurationName,
13
+ upsertRemoteConfigurationInPlace
14
+ };
@@ -18,6 +18,8 @@ export interface LiveSyncTrysteroReplicatorEnv extends LiveSyncReplicatorEnv {
18
18
  export declare class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
19
19
  private _p2pHost?;
20
20
  private _replicator?;
21
+ private _lifecycleOperation;
22
+ private _shouldBeOpen;
21
23
  get openReplicationUI(): (showResult: boolean) => Promise<boolean | void>;
22
24
  get rawReplicator(): TrysteroReplicator;
23
25
  get rawHost(): P2PHost;
@@ -25,6 +27,7 @@ export declare class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicat
25
27
  getReplicationPBKDF2Salt(_setting: RemoteDBSettings, _refresh?: boolean): Promise<Uint8Array<ArrayBuffer>>;
26
28
  terminateSync(): void;
27
29
  private _buildEnv;
30
+ private _enqueueLifecycleOperation;
28
31
  open(): Promise<void>;
29
32
  close(): Promise<void>;
30
33
  closeReplication(): void;
@@ -36,7 +39,7 @@ export declare class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicat
36
39
  onNewPeer(peer: Advertisement): Promise<void>;
37
40
  onPeerLeaved(peerId: string): void;
38
41
  replicateFromCommand(showResult?: boolean): Promise<void>;
39
- replicateFrom(peerId: string, showNotice?: boolean): Promise<{
42
+ replicateFrom(peerId: string, showNotice?: boolean, skipOrdinaryReplicationPolicy?: boolean): Promise<{
40
43
  error: any;
41
44
  ok?: undefined;
42
45
  } | {
@@ -18,6 +18,8 @@ import { delay } from "octagonal-wheels/promises";
18
18
  class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
19
19
  _p2pHost;
20
20
  _replicator;
21
+ _lifecycleOperation = Promise.resolve();
22
+ _shouldBeOpen = false;
21
23
  get openReplicationUI() {
22
24
  return this.env.openReplicationUI;
23
25
  }
@@ -59,6 +61,7 @@ class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
59
61
  return services.API.confirm;
60
62
  },
61
63
  runFiniteReplicationActivity: (task, options) => services.replicator.runFiniteReplicationActivity(task, options),
64
+ canStartOrdinaryReplication: (showMessage = false) => services.replication.onCheckReplicationReady(showMessage),
62
65
  processReplicatedDocs: async (docs) => {
63
66
  const settings = services.setting.currentSettings();
64
67
  if (settings.suspendParseReplicationResult) {
@@ -75,36 +78,48 @@ class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
75
78
  }
76
79
  };
77
80
  }
81
+ _enqueueLifecycleOperation(operation) {
82
+ const queued = this._lifecycleOperation.catch(() => void 0).then(operation);
83
+ this._lifecycleOperation = queued.catch(() => void 0);
84
+ return queued;
85
+ }
78
86
  async open() {
79
87
  if (!this.env.services.setting.currentSettings().P2P_Enabled) {
80
88
  Logger(this.translate("P2P.NotEnabled"), LOG_LEVEL_NOTICE);
81
89
  return;
82
90
  }
83
- if (this._replicator && this._p2pHost?.isServing) {
84
- Logger("P2P replicator is already open.");
85
- return;
86
- }
87
- try {
88
- const env = this._buildEnv();
89
- const host = new P2PHost(env);
90
- const replicator = new TrysteroReplicator(env, host);
91
- this._p2pHost = host;
92
- this._replicator = replicator;
93
- await replicator.open();
94
- } catch (e) {
95
- Logger(e instanceof Error ? e.message : "Error while opening P2P connection", LOG_LEVEL_NOTICE);
96
- Logger(e, LOG_LEVEL_VERBOSE);
97
- this._p2pHost = void 0;
98
- this._replicator = void 0;
99
- }
91
+ this._shouldBeOpen = true;
92
+ await this._enqueueLifecycleOperation(async () => {
93
+ if (!this._shouldBeOpen) return;
94
+ if (this._replicator && this._p2pHost?.isServing) {
95
+ Logger("P2P replicator is already open.");
96
+ return;
97
+ }
98
+ try {
99
+ const env = this._buildEnv();
100
+ const host = new P2PHost(env);
101
+ const replicator = new TrysteroReplicator(env, host);
102
+ this._p2pHost = host;
103
+ this._replicator = replicator;
104
+ await replicator.open();
105
+ } catch (e) {
106
+ Logger(e instanceof Error ? e.message : "Error while opening P2P connection", LOG_LEVEL_NOTICE);
107
+ Logger(e, LOG_LEVEL_VERBOSE);
108
+ this._p2pHost = void 0;
109
+ this._replicator = void 0;
110
+ }
111
+ });
100
112
  }
101
113
  async close() {
102
- if (this._replicator) {
103
- this._replicator.disableBroadcastChanges();
104
- await this._replicator.close();
105
- this._replicator = void 0;
106
- }
107
- this._p2pHost = void 0;
114
+ this._shouldBeOpen = false;
115
+ await this._enqueueLifecycleOperation(async () => {
116
+ if (this._replicator) {
117
+ this._replicator.disableBroadcastChanges();
118
+ await this._replicator.close();
119
+ this._replicator = void 0;
120
+ }
121
+ this._p2pHost = void 0;
122
+ });
108
123
  }
109
124
  closeReplication() {
110
125
  this._replicator?.disconnectFromServer();
@@ -138,11 +153,11 @@ class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
138
153
  { label: "replication" }
139
154
  );
140
155
  }
141
- async replicateFrom(peerId, showNotice = false) {
156
+ async replicateFrom(peerId, showNotice = false, skipOrdinaryReplicationPolicy = false) {
142
157
  const replicator = this._replicator;
143
158
  if (!replicator) throw new Error("P2P replicator is not open");
144
159
  return await this.env.services.replicator.runFiniteReplicationActivity(
145
- () => replicator.replicateFrom(peerId, showNotice),
160
+ () => skipOrdinaryReplicationPolicy ? replicator.replicateFrom(peerId, showNotice, false, true) : replicator.replicateFrom(peerId, showNotice),
146
161
  { label: "replication" }
147
162
  );
148
163
  }
@@ -303,7 +318,7 @@ class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
303
318
  }
304
319
  this.env.services.setting.currentSettings().P2P_RebuildFrom = "";
305
320
  Logger("Fetching from peer " + peerId + ".", logLevel);
306
- const rep = await r.replicateFrom(peerId, showingNotice);
321
+ const rep = await r.replicateFrom(peerId, showingNotice, false, true);
307
322
  if (rep.ok) {
308
323
  Logger("P2P Fetching has been succeed from " + peerId + ".", logLevel);
309
324
  return true;
@@ -2,11 +2,7 @@ import type { NecessaryServices } from "../../interfaces/ServiceModule.js";
2
2
  import type { P2PPaneParams } from "./UseP2PReplicatorResult.js";
3
3
  export type P2PViewFactory = (leaf: unknown) => unknown;
4
4
  /**
5
- * ServiceFeature: P2P Replicator lifecycle management.
6
- * Binds a LiveSyncTrysteroReplicator to the host's lifecycle events,
7
- * following the same middleware style as useOfflineScanner.
8
- *
9
- * @param viewTypeAndFactory Optional [viewType, factory] pair for registering the P2P pane view.
10
- * When provided, also registers commands and ribbon icon via services.API.
5
+ * @deprecated Compose `useP2PReplicatorFeature` with host-specific commands
6
+ * and UI instead. This wrapper remains for source compatibility.
11
7
  */
12
8
  export declare function useP2PReplicator(host: NecessaryServices<"API" | "appLifecycle" | "setting" | "vault" | "database" | "databaseEvents" | "keyValueDB" | "replication" | "config" | "UI" | "replicator" | "remote", never>, viewTypeAndFactory?: [viewType: string, factory: P2PViewFactory]): P2PPaneParams;