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

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 (141) hide show
  1. package/README.md +90 -20
  2. package/dist/API/DirectFileManipulatorV2.d.ts +3 -0
  3. package/dist/common/configForDoc.d.ts +1 -0
  4. package/dist/common/configForDoc.js +24 -7
  5. package/dist/common/models/setting.const.defaults.d.ts +19 -0
  6. package/dist/common/models/setting.const.defaults.js +21 -2
  7. package/dist/common/models/setting.const.preferred.js +1 -2
  8. package/dist/common/models/setting.lifecycle.d.ts +33 -0
  9. package/dist/common/models/setting.lifecycle.js +121 -0
  10. package/dist/common/models/setting.type.d.ts +10 -4
  11. package/dist/common/models/shared.definition.configNames.js +3 -3
  12. package/dist/common/models/tweak.definition.d.ts +1 -1
  13. package/dist/common/models/tweak.definition.js +1 -6
  14. package/dist/common/settingConstants.d.ts +3 -2
  15. package/dist/common/settingConstants.js +6 -6
  16. package/dist/common/types.d.ts +4 -2
  17. package/dist/common/types.js +16 -1
  18. package/dist/context.d.ts +2 -1
  19. package/dist/context.js +4 -0
  20. package/dist/interfaces/Confirm.d.ts +10 -2
  21. package/dist/interfaces/DatabaseFileAccess.d.ts +8 -0
  22. package/dist/interfaces/DatabaseRebuilder.d.ts +12 -2
  23. package/dist/interfaces/FileReflectionProvenance.d.ts +32 -0
  24. package/dist/interfaces/FileReflectionProvenance.js +25 -0
  25. package/dist/managers/ConflictManager.js +42 -9
  26. package/dist/managers/EntryManager/EntryManager.d.ts +1 -0
  27. package/dist/managers/EntryManager/EntryManager.js +5 -1
  28. package/dist/managers/EntryManager/EntryManagerImpls.d.ts +1 -0
  29. package/dist/managers/EntryManager/EntryManagerImpls.js +29 -1
  30. package/dist/platform/node/storage.d.ts +10 -1
  31. package/dist/platform/node/storage.js +86 -25
  32. package/dist/pouchdb/LiveSyncLocalDB.d.ts +1 -0
  33. package/dist/pouchdb/LiveSyncLocalDB.js +3 -0
  34. package/dist/remoteConfigurations.d.ts +9 -0
  35. package/dist/remoteConfigurations.js +14 -0
  36. package/dist/replication/LiveSyncAbstractReplicator.d.ts +3 -3
  37. package/dist/replication/couchdb/LiveSyncReplicator.d.ts +0 -1
  38. package/dist/replication/couchdb/LiveSyncReplicator.js +0 -3
  39. package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +0 -1
  40. package/dist/replication/journal/LiveSyncJournalReplicator.js +0 -3
  41. package/dist/replication/trystero/LiveSyncTrysteroReplicator.d.ts +5 -3
  42. package/dist/replication/trystero/LiveSyncTrysteroReplicator.js +42 -30
  43. package/dist/replication/trystero/P2PReplicatorCore.d.ts +2 -6
  44. package/dist/replication/trystero/P2PReplicatorCore.js +16 -86
  45. package/dist/replication/trystero/TrysteroReplicator.d.ts +2 -1
  46. package/dist/replication/trystero/TrysteroReplicator.js +10 -4
  47. package/dist/replication/trystero/TrysteroReplicatorP2PServer.d.ts +4 -5
  48. package/dist/replication/trystero/TrysteroReplicatorP2PServer.js +22 -30
  49. package/dist/replication/trystero/UseP2PReplicatorResult.d.ts +10 -2
  50. package/dist/replication/trystero/addP2PEventHandlers.d.ts +4 -2
  51. package/dist/replication/trystero/addP2PEventHandlers.js +9 -10
  52. package/dist/replication/trystero/types.d.ts +2 -0
  53. package/dist/replication/trystero/useP2PReplicatorCommands.d.ts +1 -1
  54. package/dist/replication/trystero/useP2PReplicatorCommands.js +3 -1
  55. package/dist/replication/trystero/useP2PReplicatorFeature.js +25 -15
  56. package/dist/rpc/RpcRoom.d.ts +1 -1
  57. package/dist/rpc/RpcRoom.js +22 -16
  58. package/dist/rpc/transports/TrysteroTransport.d.ts +2 -0
  59. package/dist/rpc/transports/TrysteroTransport.js +24 -17
  60. package/dist/rpc/transports/trysteroRoomEvents.d.ts +14 -0
  61. package/dist/rpc/transports/trysteroRoomEvents.js +34 -0
  62. package/dist/serviceFeatures/checkRemoteSize.d.ts +5 -3
  63. package/dist/serviceFeatures/checkRemoteSize.js +51 -11
  64. package/dist/serviceFeatures/remoteConfig.d.ts +37 -1
  65. package/dist/serviceFeatures/remoteConfig.js +90 -0
  66. package/dist/serviceModules/Rebuilder.d.ts +3 -6
  67. package/dist/serviceModules/Rebuilder.js +31 -22
  68. package/dist/serviceModules/ServiceDatabaseFileAccessBase.d.ts +5 -0
  69. package/dist/serviceModules/ServiceDatabaseFileAccessBase.js +69 -13
  70. package/dist/serviceModules/ServiceFileHandlerBase.d.ts +20 -0
  71. package/dist/serviceModules/ServiceFileHandlerBase.js +224 -40
  72. package/dist/services/BrowserServices.d.ts +2 -0
  73. package/dist/services/BrowserServices.js +4 -1
  74. package/dist/services/HeadlessServices.d.ts +2 -0
  75. package/dist/services/HeadlessServices.js +2 -1
  76. package/dist/services/base/CommonlibMessages.d.ts +318 -0
  77. package/dist/services/base/CommonlibMessages.js +313 -0
  78. package/dist/services/base/IService.d.ts +13 -6
  79. package/dist/services/base/KeyValueDBService.d.ts +1 -1
  80. package/dist/services/base/KeyValueDBService.js +3 -1
  81. package/dist/services/base/MessageTranslator.d.ts +5 -2
  82. package/dist/services/base/MessageTranslator.js +21 -0
  83. package/dist/services/base/ReplicationService.d.ts +4 -2
  84. package/dist/services/base/ReplicationService.js +11 -16
  85. package/dist/services/base/ServiceBase.d.ts +1 -1
  86. package/dist/services/base/ServiceBase.js +2 -4
  87. package/dist/services/base/SettingService.d.ts +17 -9
  88. package/dist/services/base/SettingService.js +38 -19
  89. package/dist/services/implements/headless/HeadlessAPIService.d.ts +3 -3
  90. package/dist/services/implements/headless/HeadlessAPIService.js +2 -2
  91. package/dist/services/implements/injectable/InjectableTweakValueService.d.ts +3 -3
  92. package/dist/settings.d.ts +10 -0
  93. package/dist/settings.js +32 -0
  94. package/dist/worker/bgWorker.js +1 -1
  95. package/docs/conflict-resolution.md +161 -0
  96. package/docs/development.md +106 -7
  97. package/docs/p2p-transport-lifecycle.md +62 -0
  98. package/docs/platform-storage.md +4 -0
  99. package/docs/proven-in-use.md +65 -0
  100. package/docs/releasing.md +26 -6
  101. package/docs/remote-configurations.md +63 -0
  102. package/docs/settings-lifecycle.md +85 -0
  103. package/package.json +17 -27
  104. package/dist/common/i18n.d.ts +0 -18
  105. package/dist/common/i18n.js +0 -7193
  106. package/dist/common/messages/combinedMessages.dev.d.ts +0 -7
  107. package/dist/common/messages/combinedMessages.dev.js +0 -44
  108. package/dist/common/messages/combinedMessages.prod.d.ts +0 -9264
  109. package/dist/common/messages/combinedMessages.prod.js +0 -9271
  110. package/dist/common/messages/de.d.ts +0 -297
  111. package/dist/common/messages/de.js +0 -7
  112. package/dist/common/messages/def.d.ts +0 -1119
  113. package/dist/common/messages/def.js +0 -7
  114. package/dist/common/messages/es.d.ts +0 -689
  115. package/dist/common/messages/es.js +0 -7
  116. package/dist/common/messages/fr.d.ts +0 -582
  117. package/dist/common/messages/fr.js +0 -7
  118. package/dist/common/messages/he.d.ts +0 -583
  119. package/dist/common/messages/he.js +0 -7
  120. package/dist/common/messages/ja.d.ts +0 -838
  121. package/dist/common/messages/ja.js +0 -7
  122. package/dist/common/messages/ko.d.ts +0 -800
  123. package/dist/common/messages/ko.js +0 -7
  124. package/dist/common/messages/ru.d.ts +0 -858
  125. package/dist/common/messages/ru.js +0 -7
  126. package/dist/common/messages/zh-tw.d.ts +0 -384
  127. package/dist/common/messages/zh-tw.js +0 -7
  128. package/dist/common/messages/zh.d.ts +0 -1093
  129. package/dist/common/messages/zh.js +0 -7
  130. package/dist/common/messagesJson/de.json +0 -295
  131. package/dist/common/messagesJson/en.json +0 -1117
  132. package/dist/common/messagesJson/es.json +0 -687
  133. package/dist/common/messagesJson/fr.json +0 -580
  134. package/dist/common/messagesJson/he.json +0 -581
  135. package/dist/common/messagesJson/ja.json +0 -836
  136. package/dist/common/messagesJson/ko.json +0 -798
  137. package/dist/common/messagesJson/ru.json +0 -856
  138. package/dist/common/messagesJson/zh-tw.json +0 -382
  139. package/dist/common/messagesJson/zh.json +0 -1091
  140. package/dist/common/rosetta.d.ts +0 -42
  141. package/dist/common/rosetta.js +0 -57
@@ -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
+ };
@@ -1,6 +1,7 @@
1
1
  import { type EntryDoc, type DatabaseConnectingStatus, type RemoteDBSettings, type EntryLeaf, type TweakValues, type NodeData } from "../common/types.js";
2
2
  import type { RequiredServices } from "../interfaces/ServiceModule.js";
3
3
  import type { TranslationParameters } from "../services/base/MessageTranslator.js";
4
+ import type { CommonlibMessageKey } from "../services/base/CommonlibMessages.js";
4
5
  export type ReplicationCallback = (e: PouchDB.Core.ExistingDocument<EntryDoc>[]) => Promise<boolean> | boolean;
5
6
  export type ReplicationStat = {
6
7
  sent: number;
@@ -34,11 +35,10 @@ export declare abstract class LiveSyncAbstractReplicator {
34
35
  remoteLockedAndDeviceNotAccepted: boolean;
35
36
  tweakSettingsMismatched: boolean;
36
37
  preferredTweakValue?: TweakValues;
37
- abstract get isChunkSendingSupported(): boolean;
38
38
  get database(): import("../pouchdb/LiveSyncLocalDB.js").LiveSyncLocalDB;
39
39
  get rawDatabase(): PouchDB.Database<EntryDoc>;
40
40
  get currentSettings(): import("../common/types.js").ObsidianLiveSyncSettings;
41
- protected translate<TKey extends string>(key: TKey, params?: TranslationParameters): string;
41
+ protected translate(key: CommonlibMessageKey, params?: TranslationParameters): string;
42
42
  sendChunks(setting: RemoteDBSettings, remoteDB: PouchDB.Database<EntryDoc> | undefined, showResult: boolean, fromSeq?: number | string): Promise<boolean>;
43
43
  abstract getReplicationPBKDF2Salt(setting: RemoteDBSettings, refresh?: boolean): Promise<Uint8Array<ArrayBuffer>>;
44
44
  ensurePBKDF2Salt(setting: RemoteDBSettings, showMessage?: boolean, useCache?: boolean): Promise<boolean>;
@@ -49,7 +49,7 @@ export declare abstract class LiveSyncAbstractReplicator {
49
49
  abstract openReplication(setting: RemoteDBSettings, keepAlive: boolean, showResult: boolean, ignoreCleanLock: boolean): Promise<void | boolean>;
50
50
  updateInfo: () => void;
51
51
  abstract tryConnectRemote(setting: RemoteDBSettings, showResult?: boolean): Promise<boolean>;
52
- abstract replicateAllToServer(setting: RemoteDBSettings, showingNotice?: boolean, sendChunksInBulkDisabled?: boolean): Promise<boolean>;
52
+ abstract replicateAllToServer(setting: RemoteDBSettings, showingNotice?: boolean): Promise<boolean>;
53
53
  abstract replicateAllFromServer(setting: RemoteDBSettings, showingNotice?: boolean): Promise<boolean>;
54
54
  abstract closeReplication(): void;
55
55
  abstract tryResetRemoteDatabase(setting: RemoteDBSettings): Promise<void>;
@@ -5,7 +5,6 @@ export interface LiveSyncCouchDBReplicatorEnv extends LiveSyncReplicatorEnv {
5
5
  services: ServiceHub;
6
6
  }
7
7
  export declare class LiveSyncCouchDBReplicator extends LiveSyncAbstractReplicator {
8
- get isChunkSendingSupported(): boolean;
9
8
  isMobile(): boolean;
10
9
  constructor(env: LiveSyncCouchDBReplicatorEnv);
11
10
  getInitialSyncParameters(setting: RemoteDBSettings): Promise<SyncParameters>;
@@ -85,9 +85,6 @@ async function* genReplication(s, signal) {
85
85
  }
86
86
  }
87
87
  class LiveSyncCouchDBReplicator extends LiveSyncAbstractReplicator {
88
- get isChunkSendingSupported() {
89
- return true;
90
- }
91
88
  isMobile() {
92
89
  return this.env.services.API.isMobile();
93
90
  }
@@ -7,7 +7,6 @@ import { type SimpleStore } from "../../common/utils.js";
7
7
  import type { LiveSyncJournalReplicatorEnv } from "./LiveSyncJournalReplicatorEnv.js";
8
8
  export declare class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
9
9
  env: LiveSyncJournalReplicatorEnv;
10
- get isChunkSendingSupported(): boolean;
11
10
  get client(): JournalSyncCore;
12
11
  get simpleStore(): SimpleStore<CheckPointInfo>;
13
12
  _client: JournalSyncCore;
@@ -19,9 +19,6 @@ const currentVersionRange = {
19
19
  current: 2
20
20
  };
21
21
  class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
22
- get isChunkSendingSupported() {
23
- return false;
24
- }
25
22
  get client() {
26
23
  return this.setupJournalSyncClient();
27
24
  }
@@ -18,13 +18,15 @@ 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;
24
- get isChunkSendingSupported(): boolean;
25
26
  getReplicationPBKDF2Salt(_setting: RemoteDBSettings, _refresh?: boolean): Promise<Uint8Array<ArrayBuffer>>;
26
27
  terminateSync(): void;
27
28
  private _buildEnv;
29
+ private _enqueueLifecycleOperation;
28
30
  open(): Promise<void>;
29
31
  close(): Promise<void>;
30
32
  closeReplication(): void;
@@ -36,7 +38,7 @@ export declare class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicat
36
38
  onNewPeer(peer: Advertisement): Promise<void>;
37
39
  onPeerLeaved(peerId: string): void;
38
40
  replicateFromCommand(showResult?: boolean): Promise<void>;
39
- replicateFrom(peerId: string, showNotice?: boolean): Promise<{
41
+ replicateFrom(peerId: string, showNotice?: boolean, skipOrdinaryReplicationPolicy?: boolean): Promise<{
40
42
  error: any;
41
43
  ok?: undefined;
42
44
  } | {
@@ -67,7 +69,7 @@ export declare class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicat
67
69
  makeSureOpened(): Promise<void>;
68
70
  openReplication(_setting: RemoteDBSettings, _keepAlive: boolean, showResult: boolean, _ignoreCleanLock: boolean): Promise<void | boolean>;
69
71
  tryConnectRemote(_setting: RemoteDBSettings, _showResult?: boolean): Promise<boolean>;
70
- replicateAllToServer(_setting: RemoteDBSettings, _showingNotice?: boolean, _sendChunksInBulkDisabled?: boolean): Promise<boolean>;
72
+ replicateAllToServer(_setting: RemoteDBSettings, _showingNotice?: boolean): Promise<boolean>;
71
73
  selectPeer(settingPeerName: string, r: TrysteroReplicator, logLevel: LOG_LEVEL): Promise<string | false>;
72
74
  tryUntilSuccess<T>(func: () => Promise<T | false>, repeat: number, logLevel: LOG_LEVEL): Promise<T | false>;
73
75
  replicateAllFromServer(setting: RemoteDBSettings, showingNotice?: boolean): Promise<boolean>;
@@ -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
  }
@@ -27,9 +29,6 @@ class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
27
29
  get rawHost() {
28
30
  return this._p2pHost;
29
31
  }
30
- get isChunkSendingSupported() {
31
- return false;
32
- }
33
32
  getReplicationPBKDF2Salt(_setting, _refresh) {
34
33
  return Promise.resolve(new Uint8Array(32));
35
34
  }
@@ -59,6 +58,7 @@ class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
59
58
  return services.API.confirm;
60
59
  },
61
60
  runFiniteReplicationActivity: (task, options) => services.replicator.runFiniteReplicationActivity(task, options),
61
+ canStartOrdinaryReplication: (showMessage = false) => services.replication.onCheckReplicationReady(showMessage),
62
62
  processReplicatedDocs: async (docs) => {
63
63
  const settings = services.setting.currentSettings();
64
64
  if (settings.suspendParseReplicationResult) {
@@ -75,36 +75,48 @@ class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
75
75
  }
76
76
  };
77
77
  }
78
+ _enqueueLifecycleOperation(operation) {
79
+ const queued = this._lifecycleOperation.catch(() => void 0).then(operation);
80
+ this._lifecycleOperation = queued.catch(() => void 0);
81
+ return queued;
82
+ }
78
83
  async open() {
79
84
  if (!this.env.services.setting.currentSettings().P2P_Enabled) {
80
85
  Logger(this.translate("P2P.NotEnabled"), LOG_LEVEL_NOTICE);
81
86
  return;
82
87
  }
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
- }
88
+ this._shouldBeOpen = true;
89
+ await this._enqueueLifecycleOperation(async () => {
90
+ if (!this._shouldBeOpen) return;
91
+ if (this._replicator && this._p2pHost?.isServing) {
92
+ Logger("P2P replicator is already open.");
93
+ return;
94
+ }
95
+ try {
96
+ const env = this._buildEnv();
97
+ const host = new P2PHost(env);
98
+ const replicator = new TrysteroReplicator(env, host);
99
+ this._p2pHost = host;
100
+ this._replicator = replicator;
101
+ await replicator.open();
102
+ } catch (e) {
103
+ Logger(e instanceof Error ? e.message : "Error while opening P2P connection", LOG_LEVEL_NOTICE);
104
+ Logger(e, LOG_LEVEL_VERBOSE);
105
+ this._p2pHost = void 0;
106
+ this._replicator = void 0;
107
+ }
108
+ });
100
109
  }
101
110
  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;
111
+ this._shouldBeOpen = false;
112
+ await this._enqueueLifecycleOperation(async () => {
113
+ if (this._replicator) {
114
+ this._replicator.disableBroadcastChanges();
115
+ await this._replicator.close();
116
+ this._replicator = void 0;
117
+ }
118
+ this._p2pHost = void 0;
119
+ });
108
120
  }
109
121
  closeReplication() {
110
122
  this._replicator?.disconnectFromServer();
@@ -138,11 +150,11 @@ class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
138
150
  { label: "replication" }
139
151
  );
140
152
  }
141
- async replicateFrom(peerId, showNotice = false) {
153
+ async replicateFrom(peerId, showNotice = false, skipOrdinaryReplicationPolicy = false) {
142
154
  const replicator = this._replicator;
143
155
  if (!replicator) throw new Error("P2P replicator is not open");
144
156
  return await this.env.services.replicator.runFiniteReplicationActivity(
145
- () => replicator.replicateFrom(peerId, showNotice),
157
+ () => skipOrdinaryReplicationPolicy ? replicator.replicateFrom(peerId, showNotice, false, true) : replicator.replicateFrom(peerId, showNotice),
146
158
  { label: "replication" }
147
159
  );
148
160
  }
@@ -200,7 +212,7 @@ class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
200
212
  tryConnectRemote(_setting, _showResult) {
201
213
  return Promise.resolve(false);
202
214
  }
203
- replicateAllToServer(_setting, _showingNotice, _sendChunksInBulkDisabled) {
215
+ replicateAllToServer(_setting, _showingNotice) {
204
216
  return Promise.resolve(false);
205
217
  }
206
218
  async selectPeer(settingPeerName, r, logLevel) {
@@ -303,7 +315,7 @@ class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
303
315
  }
304
316
  this.env.services.setting.currentSettings().P2P_RebuildFrom = "";
305
317
  Logger("Fetching from peer " + peerId + ".", logLevel);
306
- const rep = await r.replicateFrom(peerId, showingNotice);
318
+ const rep = await r.replicateFrom(peerId, showingNotice, false, true);
307
319
  if (rep.ok) {
308
320
  Logger("P2P Fetching has been succeed from " + peerId + ".", logLevel);
309
321
  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;
@@ -1,110 +1,34 @@
1
- import { AutoAccepting, REMOTE_P2P } from "../../common/types.js";
2
- import { reactiveSource } from "octagonal-wheels/dataobject/reactive";
1
+ import { REMOTE_P2P } from "../../common/types.js";
2
+ import { reactiveSource } from "octagonal-wheels/dataobject/reactive_v2";
3
3
  import { EVENT_REQUEST_OPEN_P2P } from "../../events/coreEvents.js";
4
- import { LiveSyncTrysteroReplicator } from "./LiveSyncTrysteroReplicator.js";
5
- import { Logger, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "../../common/logger.js";
6
4
  import { P2PLogCollector } from "./P2PLogCollector.js";
7
- import { addP2PEventHandlers } from "./addP2PEventHandlers.js";
8
- import { compatGlobal } from "../../common/coreEnvFunctions.js";
5
+ import { useP2PReplicatorCommands } from "./useP2PReplicatorCommands.js";
6
+ import { useP2PReplicatorFeature } from "./useP2PReplicatorFeature.js";
9
7
  function useP2PReplicator(host, viewTypeAndFactory) {
10
- const env = { services: host.services };
11
- let replicator = new LiveSyncTrysteroReplicator(env);
12
- const activeReplicator = {
13
- get instance() {
14
- return replicator;
15
- }
16
- };
8
+ const activeReplicator = useP2PReplicatorFeature(host);
17
9
  const events = host.services.context.events;
18
- addP2PEventHandlers(activeReplicator.instance, events);
19
10
  const p2pLogCollector = new P2PLogCollector(events);
20
11
  const storeP2PStatusLine = reactiveSource("");
21
12
  p2pLogCollector.p2pReplicationLine.onChanged((line) => {
22
13
  storeP2PStatusLine.value = line.value;
23
14
  });
24
- host.services.appLifecycle.onResumed.addHandler(() => {
25
- const settings = host.services.setting.currentSettings();
26
- if (settings.P2P_Enabled && settings.P2P_AutoStart) {
27
- compatGlobal.setTimeout(() => void replicator.open(), 100);
28
- }
29
- return Promise.resolve(true);
30
- });
31
- host.services.appLifecycle.onUnload.addHandler(async () => {
32
- await replicator.close();
33
- return true;
34
- });
35
- host.services.appLifecycle.onSuspending.addHandler(async () => {
36
- await replicator.close();
37
- return true;
38
- });
39
- host.services.databaseEvents.onDatabaseInitialisation.addHandler(async () => {
40
- await replicator.close();
41
- return true;
42
- });
43
- host.services.setting.suspendExtraSync.addHandler(() => {
44
- const s = host.services.setting.currentSettings();
45
- if (s.remoteType !== REMOTE_P2P) {
46
- s.P2P_Enabled = false;
47
- }
48
- s.P2P_AutoAccepting = AutoAccepting.NONE;
49
- s.P2P_AutoBroadcast = false;
50
- s.P2P_AutoStart = false;
51
- s.P2P_AutoSyncPeers = "";
52
- s.P2P_AutoWatchPeers = "";
53
- return Promise.resolve(true);
54
- });
55
- host.services.replicator.getNewReplicator.addHandler(
56
- async (settingOverride = {}) => {
57
- const settings = { ...host.services.setting.currentSettings(), ...settingOverride };
58
- if (settings.remoteType == REMOTE_P2P) {
59
- try {
60
- await replicator.close();
61
- } catch (e) {
62
- Logger(`Error closing existing p2p replicator`);
63
- Logger(e, LOG_LEVEL_VERBOSE);
64
- }
65
- const newReplicator = new LiveSyncTrysteroReplicator({ services: host.services });
66
- replicator = newReplicator;
67
- return Promise.resolve(replicator);
68
- }
69
- return void 0;
70
- }
71
- );
72
15
  if (viewTypeAndFactory) {
73
16
  const [viewType, factory] = viewTypeAndFactory;
74
17
  const openPane = () => host.services.API.showWindow(viewType);
18
+ useP2PReplicatorCommands(host, activeReplicator);
75
19
  host.services.appLifecycle.onInitialise.addHandler(() => {
76
20
  host.services.API.registerWindow(viewType, factory);
77
- events.onEvent(EVENT_REQUEST_OPEN_P2P, () => {
78
- void openPane();
79
- });
21
+ events.onEvent(EVENT_REQUEST_OPEN_P2P, () => void openPane());
80
22
  host.services.API.addCommand({
81
23
  id: "open-p2p-replicator",
82
24
  name: "P2P Sync : Open P2P Replicator",
83
- callback: () => {
84
- void openPane();
85
- }
86
- });
87
- host.services.API.addCommand({
88
- id: "p2p-establish-connection",
89
- name: "P2P Sync : Connect to the Signalling Server",
90
- checkCallback: (isChecking) => {
91
- if (isChecking) return !(replicator.server?.isServing ?? false);
92
- void replicator.open();
93
- }
94
- });
95
- host.services.API.addCommand({
96
- id: "p2p-close-connection",
97
- name: "P2P Sync : Disconnect from the Signalling Server",
98
- checkCallback: (isChecking) => {
99
- if (isChecking) return replicator.server?.isServing ?? false;
100
- Logger("Closing P2P Connection", LOG_LEVEL_NOTICE);
101
- void replicator.close();
102
- }
25
+ callback: () => void openPane()
103
26
  });
104
27
  host.services.API.addCommand({
105
28
  id: "replicate-now-by-p2p",
106
29
  name: "Replicate now by P2P",
107
30
  checkCallback: (isChecking) => {
31
+ const replicator = activeReplicator.replicator;
108
32
  const settings = host.services.setting.currentSettings();
109
33
  if (isChecking) {
110
34
  if (settings.remoteType == REMOTE_P2P) return false;
@@ -119,7 +43,13 @@ function useP2PReplicator(host, viewTypeAndFactory) {
119
43
  return Promise.resolve(true);
120
44
  });
121
45
  }
122
- return { replicator, p2pLogCollector, storeP2PStatusLine };
46
+ return {
47
+ get replicator() {
48
+ return activeReplicator.replicator;
49
+ },
50
+ p2pLogCollector,
51
+ storeP2PStatusLine
52
+ };
123
53
  }
124
54
  export {
125
55
  useP2PReplicator
@@ -64,6 +64,7 @@ export declare class TrysteroReplicator {
64
64
  get confirm(): Confirm;
65
65
  get translate(): import("../../context.js").MessageTranslator;
66
66
  private runFiniteReplicationActivity;
67
+ private canStartOrdinaryReplication;
67
68
  constructor(env: ReplicatorHostEnv, server?: P2PHost);
68
69
  close(): Promise<void>;
69
70
  open(): Promise<void>;
@@ -123,7 +124,7 @@ export declare class TrysteroReplicator {
123
124
  onReplicationProgress(peerId: string, info?: ProgressInfo): boolean;
124
125
  onProgressAcknowledged(peerId: string, info?: ProgressInfo): boolean;
125
126
  acknowledgeProgress(remotePeerId: string, info?: ProgressInfo): void;
126
- replicateFrom(remotePeer: string, showNotice?: boolean, fromStart?: boolean): Promise<{
127
+ replicateFrom(remotePeer: string, showNotice?: boolean, fromStart?: boolean, skipOrdinaryReplicationPolicy?: boolean): Promise<{
127
128
  error: any;
128
129
  ok?: undefined;
129
130
  } | {
@@ -51,6 +51,9 @@ class TrysteroReplicator {
51
51
  }
52
52
  return await task();
53
53
  }
54
+ async canStartOrdinaryReplication(showMessage = false) {
55
+ return this._env.canStartOrdinaryReplication ? await this._env.canStartOrdinaryReplication(showMessage) : true;
56
+ }
54
57
  constructor(env, server) {
55
58
  this._env = env;
56
59
  if (server) {
@@ -265,6 +268,9 @@ class TrysteroReplicator {
265
268
  // }
266
269
  lastSeq = "";
267
270
  async requestSynchroniseToPeer(peerId) {
271
+ if (!await this.canStartOrdinaryReplication(false)) {
272
+ return { error: new Error("Replication is not ready") };
273
+ }
268
274
  await delay(25);
269
275
  if (!this.server) throw new Error("Server is not available");
270
276
  const conn = this.server.getConnection(peerId);
@@ -440,7 +446,10 @@ class TrysteroReplicator {
440
446
  Logger(ex, LOG_LEVEL_VERBOSE);
441
447
  });
442
448
  }
443
- async replicateFrom(remotePeer, showNotice = false, fromStart = false) {
449
+ async replicateFrom(remotePeer, showNotice = false, fromStart = false, skipOrdinaryReplicationPolicy = false) {
450
+ if (!skipOrdinaryReplicationPolicy && !await this.canStartOrdinaryReplication(showNotice)) {
451
+ return { error: new Error("Replication is not ready") };
452
+ }
444
453
  const logLevel = showNotice ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO;
445
454
  Logger(`P2P Requesting Authentication to ${remotePeer}`, logLevel, "p2p-replicator");
446
455
  if (await this.requestAuthenticate(remotePeer) !== true) {
@@ -664,9 +673,6 @@ ${info.lastSeq} / ${info.maxSeqInBatch})`,
664
673
  pauseRelayReconnection();
665
674
  sockets.forEach(([, s]) => {
666
675
  s.close();
667
- s.onclose = () => {
668
- void this.server?.dispatchConnectionStatus();
669
- };
670
676
  });
671
677
  void this.pauseServe();
672
678
  }
@@ -1,4 +1,4 @@
1
- import { type ActionSender, type Room } from "@trystero-p2p/nostr";
1
+ import { type Room } from "@trystero-p2p/nostr";
2
2
  import { type P2PSyncSetting } from "../../common/types.js";
3
3
  import { type ReplicatorHostEnv, type FullFilledDeviceInfo, type Request, type Response, type Payload, type Advertisement, type BindableObject, type BindableFunction } from "./types.js";
4
4
  import { StoredMapLike } from "../../dataobject/StoredMap.js";
@@ -56,7 +56,7 @@ export declare class TrysteroReplicatorP2PServer {
56
56
  _room?: Room;
57
57
  _serverPeerId: string;
58
58
  _activeRoomId: string;
59
- ___send?: ActionSender<Payload>;
59
+ ___send?: (data: Payload, peerId?: string) => Promise<void>;
60
60
  assignedFunctions: Map<string, BindableFunction>;
61
61
  clients: Map<string, TrysteroReplicatorP2PClient>;
62
62
  _bindingObjects: BindableObject[];
@@ -81,7 +81,7 @@ export declare class TrysteroReplicatorP2PServer {
81
81
  get settings(): P2PSyncSetting;
82
82
  get isEnabled(): boolean;
83
83
  get deviceInfo(): FullFilledDeviceInfo;
84
- _sendAdvertisement?: ActionSender<Advertisement>;
84
+ _sendAdvertisement?: (data: Advertisement, peerId?: string) => Promise<void>;
85
85
  sendAdvertisement(peerId?: string): void;
86
86
  _knownAdvertisements: Map<string, Advertisement>;
87
87
  get knownAdvertisements(): Advertisement[];
@@ -93,11 +93,10 @@ export declare class TrysteroReplicatorP2PServer {
93
93
  _acceptablePeers: Computed<[settings: P2PSyncSetting], RegExp[]>;
94
94
  _shouldDenyPeers: Computed<[settings: P2PSyncSetting], RegExp[]>;
95
95
  isAcceptablePeer(peerId: string): Promise<boolean>;
96
- __send(data: Payload, peerId: string): Promise<void[]>;
96
+ __send(data: Payload, peerId: string): Promise<void>;
97
97
  processArrivedRPC(data: Payload, peerId: string): Promise<void>;
98
98
  private _onPeerJoin;
99
99
  private _onPeerLeave;
100
- activePeer: Map<string, RTCPeerConnection>;
101
100
  onAfterJoinRoom(): void;
102
101
  startService(bindings?: BindableObject[]): Promise<void>;
103
102
  start(bindings?: BindableObject[]): Promise<void>;
@@ -14,6 +14,7 @@ import { shareRunningResult } from "octagonal-wheels/concurrency/lock_v2";
14
14
  import { Computed } from "octagonal-wheels/dataobject/Computed";
15
15
  import { RpcRoom } from "../../rpc/index.js";
16
16
  import { TRYSTERO_RPC_DEFAULTS } from "../../rpc/transports/TrysteroTransport.js";
17
+ import { subscribeTrysteroPeerEvents } from "../../rpc/transports/trysteroRoomEvents.js";
17
18
  import { toRpcMethodName } from "./rpcCompat.js";
18
19
  import { generateJoinRoomOptions } from "../../rpc/transports/trysteroUtils.js";
19
20
  import { subscribeConnectionStatus, subscribeFailureDiagnosis } from "../../rpc/transports/DiagRTCPeerConnections.js";
@@ -333,52 +334,47 @@ You can chose as follows:
333
334
  Logger(`Received peer join event from ${peerId}, but no active room. Ignoring.`, LOG_LEVEL_VERBOSE);
334
335
  return;
335
336
  }
336
- const peers = this._room.getPeers();
337
- const peer = peers[peerId];
338
337
  Logger(`Peer joined: ${peerId}`, LOG_LEVEL_VERBOSE);
339
- this.activePeer.set(peerId, peer);
340
338
  this.sendAdvertisement(peerId);
341
339
  }
342
340
  _onPeerLeave(peerId) {
343
341
  Logger(`Peer left: ${peerId}`, LOG_LEVEL_VERBOSE);
344
342
  this._knownAdvertisements.delete(peerId);
345
- const peerConn = this.activePeer.get(peerId);
346
- if (peerConn) {
347
- peerConn.close();
348
- this.activePeer.delete(peerId);
349
- }
350
343
  void this._env.events.emitEvent(EVENT_DEVICE_LEAVED, peerId);
351
344
  void this.dispatchConnectionStatus();
352
345
  }
353
- activePeer = /* @__PURE__ */ new Map();
354
346
  onAfterJoinRoom() {
355
347
  Logger(`Initializing...`, LOG_LEVEL_VERBOSE);
356
348
  const room = this.room;
357
349
  if (!room) throw new Error("This server has been already disconnected");
358
- const [sendRpc, arrivedRpc] = room.makeAction("rpc2");
350
+ const rpcAction = room.makeAction("rpc2");
359
351
  const transport = {
360
352
  send: (message, peerId) => {
361
- return sendRpc(message, peerId).then(() => void 0);
353
+ return rpcAction.send(message, { target: peerId });
362
354
  },
363
355
  onMessage: (handler) => {
364
- arrivedRpc((data, peerId) => {
356
+ rpcAction.onMessage = (data, { peerId }) => {
365
357
  handler(data, peerId);
366
- });
367
- return () => void 0;
358
+ };
359
+ return () => {
360
+ rpcAction.onMessage = null;
361
+ };
368
362
  },
369
363
  onPeerJoin: (handler) => {
370
- room.onPeerJoin((peerId) => {
371
- this._onPeerJoin(peerId);
372
- handler(peerId);
364
+ return subscribeTrysteroPeerEvents(room, {
365
+ onJoin: (peerId) => {
366
+ this._onPeerJoin(peerId);
367
+ handler(peerId);
368
+ }
373
369
  });
374
- return () => void 0;
375
370
  },
376
371
  onPeerLeave: (handler) => {
377
- room.onPeerLeave((peerId) => {
378
- this._onPeerLeave(peerId);
379
- handler(peerId);
372
+ return subscribeTrysteroPeerEvents(room, {
373
+ onLeave: (peerId) => {
374
+ this._onPeerLeave(peerId);
375
+ handler(peerId);
376
+ }
380
377
  });
381
- return () => void 0;
382
378
  }
383
379
  };
384
380
  this._rpcRoom?.close();
@@ -393,11 +389,11 @@ You can chose as follows:
393
389
  Logger(`RPC Protocol warning${peerId ? ` from ${peerId}` : ""}: ${message}`, LOG_LEVEL_VERBOSE);
394
390
  }
395
391
  });
396
- const [adSend, adArrived] = room.makeAction("ad");
397
- this._sendAdvertisement = adSend;
398
- adArrived((data, peerId) => {
392
+ const advertisementAction = room.makeAction("ad");
393
+ this._sendAdvertisement = (data, peerId) => advertisementAction.send(data, peerId ? { target: peerId } : void 0);
394
+ advertisementAction.onMessage = (data, { peerId }) => {
399
395
  void this.onAdvertisement(data, peerId);
400
- });
396
+ };
401
397
  this._env.events.emitEvent(EVENT_P2P_CONNECTED);
402
398
  void this.dispatchConnectionStatus();
403
399
  }
@@ -494,14 +490,10 @@ You can chose as follows:
494
490
  }
495
491
  async close() {
496
492
  this.assignedFunctions.clear();
497
- const peers = this.room?.getPeers() ?? {};
498
493
  this.clients.forEach((client) => client.close());
499
494
  this.clients.clear();
500
495
  this._rpcRoom?.close();
501
496
  this._rpcRoom = void 0;
502
- for (const [, peer] of Object.entries(peers)) {
503
- peer.close();
504
- }
505
497
  await this.ensureLeaved();
506
498
  this._activeRoomId = "";
507
499
  this._knownAdvertisements.clear();
@@ -2,10 +2,18 @@ import type { ReactiveSource } from "octagonal-wheels/dataobject/reactive_v2";
2
2
  import type { LiveSyncTrysteroReplicator } from "./LiveSyncTrysteroReplicator.js";
3
3
  import type { P2PLogCollector } from "./P2PLogCollector.js";
4
4
  export type UseP2PReplicatorResult = {
5
- replicator: LiveSyncTrysteroReplicator;
5
+ /**
6
+ * The current replicator selected by the host lifecycle.
7
+ *
8
+ * Read this property at the point of use. Retaining a destructured value
9
+ * across settings or database lifecycle events can target a closed,
10
+ * replaced instance.
11
+ */
12
+ readonly replicator: LiveSyncTrysteroReplicator;
6
13
  };
7
14
  export type P2PPaneParams = {
8
- replicator: LiveSyncTrysteroReplicator;
15
+ /** Current replicator; preserve the result object so its getter remains live. */
16
+ readonly replicator: LiveSyncTrysteroReplicator;
9
17
  p2pLogCollector: P2PLogCollector;
10
18
  storeP2PStatusLine: ReactiveSource<string>;
11
19
  };
@@ -17,11 +17,13 @@ export interface P2PReplicatorLike {
17
17
  isServing?: boolean;
18
18
  };
19
19
  }
20
+ /** Resolves the replicator which currently owns P2P state. */
21
+ export type P2PReplicatorProvider = () => P2PReplicatorLike;
20
22
  /**
21
23
  * Add event handlers for P2P replication related events.
22
- * @param instance P2PReplicatorLike instance
24
+ * @param source A fixed compatibility instance or a provider for a replaceable replicator.
23
25
  */
24
- export declare function addP2PEventHandlers(instance: P2PReplicatorLike, events: LiveSyncEventHub): void;
26
+ export declare function addP2PEventHandlers(source: P2PReplicatorLike | P2PReplicatorProvider, events: LiveSyncEventHub): void;
25
27
  /**
26
28
  * open P2P replicator if not opened yet.
27
29
  * @param instance