@vrtmrz/livesync-commonlib 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -1
- package/dist/API/DirectFileManipulatorV2.d.ts +7 -1
- package/dist/API/DirectFileManipulatorV2.js +46 -67
- package/dist/API/DirectFileWatchOwner.d.ts +24 -0
- package/dist/API/DirectFileWatchOwner.js +256 -0
- package/dist/API/processSetting.js +3 -3
- package/dist/ContentSplitter/ContentSplitterRabinKarp.js +2 -17
- package/dist/ContentSplitter/ContentSplitterV1.js +9 -19
- package/dist/ContentSplitter/ContentSplitterV2.js +9 -19
- package/dist/common/models/setting.type.d.ts +1 -1
- package/dist/common/models/shared.definition.configNames.js +1 -2
- package/dist/common/models/tweak.definition.d.ts +1 -1
- package/dist/common/pathNormalization.d.ts +2 -0
- package/dist/common/pathNormalization.js +7 -2
- package/dist/common/utils.d.ts +33 -3
- package/dist/common/utils.js +12 -44
- package/dist/common/utils.object.js +2 -1
- package/dist/concurrency/keyedPendingWork.d.ts +13 -0
- package/dist/concurrency/keyedPendingWork.js +27 -0
- package/dist/conflicts/conflictCheckQueue.d.ts +45 -0
- package/dist/conflicts/conflictCheckQueue.js +108 -0
- package/dist/conflicts.d.ts +3 -0
- package/dist/conflicts.js +4 -0
- package/dist/diagnostics/integrity.js +11 -12
- package/dist/interfaces/ServiceModule.d.ts +42 -24
- package/dist/interfaces/ServiceModule.js +4 -3
- package/dist/managers/ChunkFetcher.d.ts +3 -0
- package/dist/managers/ChunkFetcher.js +20 -13
- package/dist/managers/LiveSyncManagers.d.ts +2 -0
- package/dist/managers/LiveSyncManagers.js +1 -0
- package/dist/managers/StorageEventManager.d.ts +3 -3
- package/dist/managers/StorageEventManager.js +15 -6
- package/dist/managers/chunkFetchSource.d.ts +14 -0
- package/dist/managers/chunkFetchSource.js +12 -0
- package/dist/pouchdb/LiveSyncDBFunctions.js +5 -0
- package/dist/pouchdb/LiveSyncLocalDB.d.ts +7 -1
- package/dist/pouchdb/LiveSyncLocalDB.js +14 -10
- package/dist/pouchdb/chunks.d.ts +16 -0
- package/dist/pouchdb/pathIdentityCompatibility.d.ts +8 -0
- package/dist/pouchdb/pathIdentityCompatibility.js +19 -1
- package/dist/pouchdb/pouchdb-browser.js +2 -84
- package/dist/pouchdb/pouchdb-http.js +2 -84
- package/dist/pouchdb/purgeMulti.d.ts +4 -0
- package/dist/pouchdb/purgeMulti.js +89 -0
- package/dist/replication/RemoteResource.d.ts +3 -0
- package/dist/replication/index.d.ts +2 -0
- package/dist/replication/index.js +2 -0
- package/dist/replication/journal/JournalSyncCore.d.ts +1 -0
- package/dist/replication/journal/JournalSyncCore.js +41 -20
- package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +11 -3
- package/dist/replication/journal/LiveSyncJournalReplicator.js +123 -45
- package/dist/replication/trystero/P2PRoomSessionOwner.js +14 -13
- package/dist/replication/trystero/TrysteroReplicator.d.ts +7 -5
- package/dist/replication/trystero/TrysteroReplicator.js +101 -74
- package/dist/replication/trystero/TrysteroReplicator.pathIdentity.d.ts +11 -0
- package/dist/replication/trystero/TrysteroReplicator.pathIdentity.js +72 -0
- package/dist/replication/trystero/TrysteroReplicatorP2PServer.js +3 -2
- package/dist/replication/trystero/p2pReplicatorConfigurationIdentity.js +2 -0
- package/dist/replication/trystero/peerPatterns.d.ts +1 -0
- package/dist/replication/trystero/peerPatterns.js +6 -0
- package/dist/replication/trystero/useP2PReplicatorFeature.js +6 -2
- package/dist/rpc/RpcRoom.d.ts +5 -1
- package/dist/rpc/RpcRoom.js +80 -105
- package/dist/serviceFeatures/offlineScanner.d.ts +4 -4
- package/dist/serviceFeatures/offlineScanner.js +14 -15
- package/dist/serviceFeatures/prepareDatabaseForUse.d.ts +2 -2
- package/dist/serviceFeatures/remoteConfig.d.ts +10 -5
- package/dist/serviceModules/Rebuilder.d.ts +1 -0
- package/dist/serviceModules/Rebuilder.js +33 -5
- package/dist/serviceModules/ServiceFileHandlerBase.js +2 -7
- package/dist/services/implements/injectable/InjectableConflictService.d.ts +5 -5
- package/dist/services/lib/HandlerUtils.d.ts +8 -6
- package/dist/services/lib/HandlerUtils.js +14 -0
- package/dist/worker/bgWorker.js +1 -1
- package/docs/conflict-resolution.md +30 -0
- package/docs/development.md +50 -0
- package/docs/proven-in-use.md +80 -0
- package/docs/releasing.md +4 -0
- package/docs/service-feature-composition.md +63 -10
- package/docs/unicode-path-identity.md +64 -3
- package/package.json +11 -3
|
@@ -141,6 +141,7 @@ export declare class TrysteroReplicator {
|
|
|
141
141
|
setOnSetup(): void;
|
|
142
142
|
clearOnSetup(): void;
|
|
143
143
|
getTweakSettings(fromPeerId: string): Promise<{
|
|
144
|
+
pathNormalizationVersion: import("../../settings.js").PathNormalizationVersion;
|
|
144
145
|
idDerivationVersion: 0 | 1;
|
|
145
146
|
idDerivationProof: string;
|
|
146
147
|
liveSync?: boolean;
|
|
@@ -251,7 +252,6 @@ export declare class TrysteroReplicator {
|
|
|
251
252
|
bucketPrefix?: string;
|
|
252
253
|
forcePathStyle?: boolean;
|
|
253
254
|
remoteType?: import("../../common/types.js").RemoteType;
|
|
254
|
-
pathNormalizationVersion?: import("../../settings.js").PathNormalizationVersion;
|
|
255
255
|
encrypt?: boolean;
|
|
256
256
|
passphrase?: string;
|
|
257
257
|
idDerivationKey?: string;
|
|
@@ -331,6 +331,7 @@ export declare class TrysteroReplicator {
|
|
|
331
331
|
reqSync: (fromPeerId: string) => Promise<P2PReplicationWireResult>;
|
|
332
332
|
"!reqAuth": (fromPeerId: string) => Promise<boolean>;
|
|
333
333
|
getTweakSettings: (fromPeerId: string) => Promise<{
|
|
334
|
+
pathNormalizationVersion: import("../../settings.js").PathNormalizationVersion;
|
|
334
335
|
idDerivationVersion: 0 | 1;
|
|
335
336
|
idDerivationProof: string;
|
|
336
337
|
liveSync?: boolean;
|
|
@@ -441,7 +442,6 @@ export declare class TrysteroReplicator {
|
|
|
441
442
|
bucketPrefix?: string;
|
|
442
443
|
forcePathStyle?: boolean;
|
|
443
444
|
remoteType?: import("../../common/types.js").RemoteType;
|
|
444
|
-
pathNormalizationVersion?: import("../../settings.js").PathNormalizationVersion;
|
|
445
445
|
encrypt?: boolean;
|
|
446
446
|
passphrase?: string;
|
|
447
447
|
idDerivationKey?: string;
|
|
@@ -517,15 +517,15 @@ export declare class TrysteroReplicator {
|
|
|
517
517
|
P2P_useDiagRTC?: boolean;
|
|
518
518
|
}>;
|
|
519
519
|
onProgress: (fromPeerId: string) => Promise<{
|
|
520
|
-
error: import("../../rpc/
|
|
520
|
+
error: import("../../rpc/types.js").RpcErrorShape;
|
|
521
521
|
}>;
|
|
522
522
|
getAllConfig: (fromPeerId: string) => Promise<string | {
|
|
523
|
-
error: import("../../rpc/
|
|
523
|
+
error: import("../../rpc/types.js").RpcErrorShape;
|
|
524
524
|
}>;
|
|
525
525
|
onProgressAcknowledged: (fromPeerId: string, info: ProgressInfo) => Promise<void>;
|
|
526
526
|
getIsBroadcasting: () => Promise<boolean>;
|
|
527
527
|
requestBroadcasting: (peerId: string) => Promise<true | {
|
|
528
|
-
error: import("../../rpc/
|
|
528
|
+
error: import("../../rpc/types.js").RpcErrorShape;
|
|
529
529
|
}>;
|
|
530
530
|
};
|
|
531
531
|
requestAuthenticate(peerId: string, signal?: AbortSignal): Promise<boolean>;
|
|
@@ -553,6 +553,8 @@ export declare class TrysteroReplicator {
|
|
|
553
553
|
acknowledgeProgress(remotePeerId: string, info?: ProgressInfo): void;
|
|
554
554
|
replicateFrom(remotePeer: string, showNotice?: boolean, fromStart?: boolean, skipOrdinaryReplicationPolicy?: boolean, callerSignal?: AbortSignal): Promise<P2PReplicationResult>;
|
|
555
555
|
private replicateFromWithinSession;
|
|
556
|
+
private checkPeerForReplication;
|
|
557
|
+
private transferFromPeer;
|
|
556
558
|
notifyProgress(excludePeerId?: string): Promise<void>;
|
|
557
559
|
requestBroadcastChanges(peerId: string): Promise<unknown>;
|
|
558
560
|
getRemoteIsBroadcasting(peerId: string): Promise<unknown>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parsePeerPatterns } from "./peerPatterns.js";
|
|
1
2
|
import {
|
|
2
3
|
TweakValuesShouldMatchedTemplate,
|
|
3
4
|
omitP2PRuntimeSettings
|
|
@@ -27,6 +28,9 @@ import { compatGlobal } from "../../common/coreEnvFunctions.js";
|
|
|
27
28
|
import { computeKeyedId, configuredIdKey } from "../../common/idDerivation.js";
|
|
28
29
|
import { getWebCrypto } from "../../mods.js";
|
|
29
30
|
import { uint8ArrayToHexString } from "../../string_and_binary/convert.js";
|
|
31
|
+
import { getPathNormalizationVersion } from "../../common/pathNormalization.js";
|
|
32
|
+
import { readLocalP2PPathIdentity, readRemoteP2PPathIdentity } from "./TrysteroReplicator.pathIdentity.js";
|
|
33
|
+
import { assertDatabasePathIdentity } from "../../pouchdb/pathIdentityCompatibility.js";
|
|
30
34
|
import {
|
|
31
35
|
fromP2PReplicationWireResult,
|
|
32
36
|
toP2PReplicationWireResult,
|
|
@@ -171,12 +175,10 @@ class TrysteroReplicator {
|
|
|
171
175
|
}
|
|
172
176
|
}
|
|
173
177
|
get autoSyncPeers() {
|
|
174
|
-
|
|
175
|
-
return peers;
|
|
178
|
+
return parsePeerPatterns(this.currentSettings.P2P_AutoSyncPeers);
|
|
176
179
|
}
|
|
177
180
|
get autoWatchPeers() {
|
|
178
|
-
|
|
179
|
-
return peers;
|
|
181
|
+
return parsePeerPatterns(this.currentSettings.P2P_AutoWatchPeers);
|
|
180
182
|
}
|
|
181
183
|
async onNewPeer(peer) {
|
|
182
184
|
const peerName = peer.name;
|
|
@@ -206,6 +208,11 @@ class TrysteroReplicator {
|
|
|
206
208
|
this._onSetup = false;
|
|
207
209
|
}
|
|
208
210
|
async getTweakSettings(fromPeerId) {
|
|
211
|
+
const currentSettings = this.currentSettings;
|
|
212
|
+
const pathNormalizationVersion = getPathNormalizationVersion(currentSettings);
|
|
213
|
+
if (pathNormalizationVersion === 1) {
|
|
214
|
+
await assertDatabasePathIdentity(this.db, currentSettings, true);
|
|
215
|
+
}
|
|
209
216
|
const allSettings = JSON.parse(JSON.stringify(this.settings));
|
|
210
217
|
for (const key in allSettings) {
|
|
211
218
|
if (key == "encrypt") {
|
|
@@ -224,6 +231,7 @@ class TrysteroReplicator {
|
|
|
224
231
|
const idKey = configuredIdKey({ idDerivationVersion, idDerivationKey: idSettings.idDerivationKey ?? "" });
|
|
225
232
|
return {
|
|
226
233
|
...allSettings,
|
|
234
|
+
pathNormalizationVersion,
|
|
227
235
|
idDerivationVersion,
|
|
228
236
|
idDerivationProof: idKey && idSettings.encrypt && idSettings.usePathObfuscation ? await computeKeyedId(idKey, "peer-agreement", fromPeerId) : ""
|
|
229
237
|
};
|
|
@@ -554,6 +562,28 @@ class TrysteroReplicator {
|
|
|
554
562
|
}
|
|
555
563
|
if (signal?.aborted) return P2P_REPLICATION_CANCELLED;
|
|
556
564
|
const logLevel = showNotice ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO;
|
|
565
|
+
const rejection = await this.checkPeerForReplication(remotePeer, logLevel, signal);
|
|
566
|
+
if (rejection) return rejection;
|
|
567
|
+
Logger(`P2P Replicating from ${remotePeer}`, logLevel, "p2p-replicator");
|
|
568
|
+
if (this._replicateFromPeers.has(remotePeer)) {
|
|
569
|
+
Logger(`Replication from ${remotePeer} is already in progress`, logLevel, "p2p-replicator");
|
|
570
|
+
return p2pReplicationFailed(new Error("Replication from this peer is already in progress"));
|
|
571
|
+
}
|
|
572
|
+
this._replicateFromPeers.add(remotePeer);
|
|
573
|
+
this.dispatchStatus();
|
|
574
|
+
try {
|
|
575
|
+
return await this.transferFromPeer(remotePeer, fromStart, logLevel, signal);
|
|
576
|
+
} catch (e) {
|
|
577
|
+
if (signal?.aborted) return P2P_REPLICATION_CANCELLED;
|
|
578
|
+
Logger("Error while P2P replicating", logLevel, "p2p-replicator");
|
|
579
|
+
Logger(e, LOG_LEVEL_VERBOSE);
|
|
580
|
+
return p2pReplicationFailed(e);
|
|
581
|
+
} finally {
|
|
582
|
+
this._replicateFromPeers.delete(remotePeer);
|
|
583
|
+
this.dispatchStatus();
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
async checkPeerForReplication(remotePeer, logLevel, signal) {
|
|
557
587
|
Logger(`P2P Requesting Authentication to ${remotePeer}`, logLevel, "p2p-replicator");
|
|
558
588
|
const authenticated = signal ? await this.requestAuthenticate(remotePeer, signal) : await this.requestAuthenticate(remotePeer);
|
|
559
589
|
if (authenticated !== true) {
|
|
@@ -573,71 +603,62 @@ class TrysteroReplicator {
|
|
|
573
603
|
Logger("Tweak values are not matched", logLevel, "p2p-replicator");
|
|
574
604
|
return p2pReplicationFailed(new Error("Tweak values are not matched"));
|
|
575
605
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
this.
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
const
|
|
591
|
-
Logger(
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
606
|
+
return void 0;
|
|
607
|
+
}
|
|
608
|
+
async transferFromPeer(remotePeer, fromStart, logLevel, signal) {
|
|
609
|
+
if (!this.server) {
|
|
610
|
+
throw new Error("Server is not available");
|
|
611
|
+
}
|
|
612
|
+
const connection = this.server.getConnection(remotePeer);
|
|
613
|
+
const remoteDB = connection.getRemoteDB(signal);
|
|
614
|
+
const pathNormalizationVersion = getPathNormalizationVersion(this.currentSettings);
|
|
615
|
+
const [localPathIdentity, remotePathIdentity] = await Promise.all([
|
|
616
|
+
readLocalP2PPathIdentity(this.db, pathNormalizationVersion),
|
|
617
|
+
readRemoteP2PPathIdentity(remoteDB, pathNormalizationVersion)
|
|
618
|
+
]);
|
|
619
|
+
if (localPathIdentity !== pathNormalizationVersion || remotePathIdentity !== pathNormalizationVersion) {
|
|
620
|
+
const error = new Error("P2P database path identity does not match the configured path identity.");
|
|
621
|
+
Logger(error.message, logLevel, "p2p-replicator");
|
|
622
|
+
return p2pReplicationFailed(error);
|
|
623
|
+
}
|
|
624
|
+
Logger(`P2P replicateFrom preparing remote DB info for ${remotePeer}`, LOG_LEVEL_VERBOSE, "p2p-replicator");
|
|
625
|
+
const remoteDBInfo = await remoteDB.info();
|
|
626
|
+
Logger(
|
|
627
|
+
`P2P replicateFrom remote DB info for ${remotePeer}: ${remoteDBInfo.db_name} seq=${remoteDBInfo.update_seq}`,
|
|
628
|
+
LOG_LEVEL_VERBOSE,
|
|
629
|
+
"p2p-replicator"
|
|
630
|
+
);
|
|
631
|
+
const localDBInfo = await this.db.info();
|
|
632
|
+
Logger(
|
|
633
|
+
`P2P replicateFrom local DB info for ${remotePeer}: ${localDBInfo.db_name} seq=${localDBInfo.update_seq}`,
|
|
634
|
+
LOG_LEVEL_VERBOSE,
|
|
635
|
+
"p2p-replicator"
|
|
636
|
+
);
|
|
637
|
+
Logger(`P2P replicateFrom entering replicateShim for ${remotePeer}`, LOG_LEVEL_VERBOSE, "p2p-replicator");
|
|
638
|
+
const outcome = await replicateShim(
|
|
639
|
+
this.db,
|
|
640
|
+
remoteDB,
|
|
641
|
+
async (docs, info) => {
|
|
642
|
+
await this._env.processReplicatedDocs(docs);
|
|
643
|
+
void this.dispatchReplicationProgress(remotePeer, info);
|
|
644
|
+
void this.acknowledgeProgress(remotePeer, info);
|
|
645
|
+
void this.notifyProgress(remotePeer);
|
|
646
|
+
Logger(
|
|
647
|
+
`P2P Replication from ${remotePeer}
|
|
613
648
|
${info.lastSeq} / ${info.maxSeqInBatch})`,
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
Logger(`P2P replicateFrom replicateShim returned for ${remotePeer}`, LOG_LEVEL_VERBOSE, "p2p-replicator");
|
|
626
|
-
void this.acknowledgeProgress(remotePeer, void 0);
|
|
627
|
-
if (outcome.status === "cancelled") {
|
|
628
|
-
Logger(`P2P Replication from ${remotePeer} has been cancelled`, logLevel, "p2p-replicator");
|
|
629
|
-
return P2P_REPLICATION_CANCELLED;
|
|
630
|
-
}
|
|
631
|
-
Logger(`P2P Replication from ${remotePeer} has been completed`, logLevel, "p2p-replicator");
|
|
632
|
-
} catch (e) {
|
|
633
|
-
if (signal?.aborted) return P2P_REPLICATION_CANCELLED;
|
|
634
|
-
Logger("Error while P2P replicating", logLevel, "p2p-replicator");
|
|
635
|
-
Logger(e, LOG_LEVEL_VERBOSE);
|
|
636
|
-
return p2pReplicationFailed(e);
|
|
637
|
-
} finally {
|
|
638
|
-
this._replicateFromPeers.delete(remotePeer);
|
|
639
|
-
this.dispatchStatus();
|
|
649
|
+
logLevel,
|
|
650
|
+
"p2p-replicator"
|
|
651
|
+
);
|
|
652
|
+
},
|
|
653
|
+
{ live: false, rewind: fromStart, signal }
|
|
654
|
+
);
|
|
655
|
+
Logger(`P2P replicateFrom replicateShim returned for ${remotePeer}`, LOG_LEVEL_VERBOSE, "p2p-replicator");
|
|
656
|
+
void this.acknowledgeProgress(remotePeer, void 0);
|
|
657
|
+
if (outcome.status === "cancelled") {
|
|
658
|
+
Logger(`P2P Replication from ${remotePeer} has been cancelled`, logLevel, "p2p-replicator");
|
|
659
|
+
return P2P_REPLICATION_CANCELLED;
|
|
640
660
|
}
|
|
661
|
+
Logger(`P2P Replication from ${remotePeer} has been completed`, logLevel, "p2p-replicator");
|
|
641
662
|
return P2P_REPLICATION_COMPLETED;
|
|
642
663
|
}
|
|
643
664
|
notifyProgress(excludePeerId) {
|
|
@@ -740,12 +761,6 @@ ${info.lastSeq} / ${info.maxSeqInBatch})`,
|
|
|
740
761
|
Logger("Peer is not found", logLevel);
|
|
741
762
|
return false;
|
|
742
763
|
}
|
|
743
|
-
if (this.platform === "pseudo-replicator") {
|
|
744
|
-
return true;
|
|
745
|
-
}
|
|
746
|
-
if (peerPlatform === "pseudo-replicator") {
|
|
747
|
-
return true;
|
|
748
|
-
}
|
|
749
764
|
const connection = this.server.getConnection(peerId);
|
|
750
765
|
const challengeBytes = new Uint8Array(16);
|
|
751
766
|
(await getWebCrypto()).getRandomValues(challengeBytes);
|
|
@@ -753,6 +768,15 @@ ${info.lastSeq} / ${info.maxSeqInBatch})`,
|
|
|
753
768
|
const tweakValues = await connection.invokeRemoteObjectFunction("getTweakSettings", [challenge], 5e3, signal);
|
|
754
769
|
if (signal?.aborted) return false;
|
|
755
770
|
const thisTweakValues = await this.getTweakSettings(challenge);
|
|
771
|
+
const localPathNormalizationVersion = getPathNormalizationVersion(thisTweakValues);
|
|
772
|
+
const peerPathNormalizationVersion = getPathNormalizationVersion(tweakValues);
|
|
773
|
+
if (localPathNormalizationVersion !== peerPathNormalizationVersion) {
|
|
774
|
+
Logger("Replication cancelled: The path identity mode does not match this peer.", logLevel);
|
|
775
|
+
return false;
|
|
776
|
+
}
|
|
777
|
+
if (this.platform === "pseudo-replicator" || peerPlatform === "pseudo-replicator") {
|
|
778
|
+
return true;
|
|
779
|
+
}
|
|
756
780
|
if (thisTweakValues.passphrase !== tweakValues.passphrase) {
|
|
757
781
|
Logger(
|
|
758
782
|
"Replication cancelled: Passphrase is not matched\nCannot replicate to a remote database until the problem is resolved.",
|
|
@@ -761,7 +785,10 @@ ${info.lastSeq} / ${info.maxSeqInBatch})`,
|
|
|
761
785
|
return false;
|
|
762
786
|
}
|
|
763
787
|
if (thisTweakValues.encrypt !== tweakValues.encrypt || thisTweakValues.usePathObfuscation !== tweakValues.usePathObfuscation) {
|
|
764
|
-
Logger(
|
|
788
|
+
Logger(
|
|
789
|
+
"Replication cancelled: The encryption or path obfuscation settings do not match this peer.",
|
|
790
|
+
logLevel
|
|
791
|
+
);
|
|
765
792
|
return false;
|
|
766
793
|
}
|
|
767
794
|
if (thisTweakValues.encrypt && thisTweakValues.usePathObfuscation && ((thisTweakValues.idDerivationVersion ?? 0) !== (tweakValues.idDerivationVersion ?? 0) || (thisTweakValues.idDerivationProof ?? "") !== (tweakValues.idDerivationProof ?? ""))) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PathNormalizationVersion } from "../../common/models/setting.type.js";
|
|
2
|
+
export declare function readLocalP2PPathIdentity(database: {
|
|
3
|
+
get(id: string): Promise<unknown>;
|
|
4
|
+
}, configuredVersion: PathNormalizationVersion): Promise<PathNormalizationVersion>;
|
|
5
|
+
export declare function readRemoteP2PPathIdentity(database: {
|
|
6
|
+
bulkGet(options: {
|
|
7
|
+
docs: {
|
|
8
|
+
id: string;
|
|
9
|
+
}[];
|
|
10
|
+
}): Promise<unknown>;
|
|
11
|
+
}, configuredVersion: PathNormalizationVersion): Promise<PathNormalizationVersion>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { VERSIONING_DOCID } from "../../common/models/db.const.js";
|
|
2
|
+
import {
|
|
3
|
+
assessRemoteFeatureDocument,
|
|
4
|
+
describeRemoteFeatureRejection,
|
|
5
|
+
NFC_PATH_IDENTITY_FEATURE
|
|
6
|
+
} from "../../pouchdb/remoteFeatureCompatibility.js";
|
|
7
|
+
import { isErrorOfMissingDoc } from "../../pouchdb/utils_couchdb.js";
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
function hasOwnField(value, key) {
|
|
12
|
+
return Object.hasOwn(value, key);
|
|
13
|
+
}
|
|
14
|
+
function isVersionInfoResponse(value) {
|
|
15
|
+
if (!isRecord(value) || !Array.isArray(value.results) || value.results.length !== 1) return false;
|
|
16
|
+
const [result] = value.results;
|
|
17
|
+
return isRecord(result) && result.id === VERSIONING_DOCID && Array.isArray(result.docs) && result.docs.length === 1 && isRecord(result.docs[0]);
|
|
18
|
+
}
|
|
19
|
+
function isMissingDocumentResult(result) {
|
|
20
|
+
if (!hasOwnField(result, "error")) return false;
|
|
21
|
+
const error = result.error;
|
|
22
|
+
const errorDetails = isRecord(error) ? error : void 0;
|
|
23
|
+
const status = errorDetails?.status ?? result.status;
|
|
24
|
+
const errorName = errorDetails?.name ?? result.name;
|
|
25
|
+
const errorCode = typeof error === "string" ? error : errorDetails?.error;
|
|
26
|
+
return status === 404 || status === void 0 && (errorName === "not_found" || errorCode === "not_found");
|
|
27
|
+
}
|
|
28
|
+
function parseRemotePathIdentityDocument(response) {
|
|
29
|
+
if (!isVersionInfoResponse(response)) {
|
|
30
|
+
throw new Error("P2P remote versioninfo response is malformed.");
|
|
31
|
+
}
|
|
32
|
+
const [documentResult] = response.results[0].docs;
|
|
33
|
+
if (hasOwnField(documentResult, "ok")) {
|
|
34
|
+
const document = documentResult.ok;
|
|
35
|
+
if (!isRecord(document) || document._id !== VERSIONING_DOCID) {
|
|
36
|
+
throw new Error("P2P remote versioninfo response is malformed.");
|
|
37
|
+
}
|
|
38
|
+
return { kind: "found", document };
|
|
39
|
+
}
|
|
40
|
+
if (isMissingDocumentResult(documentResult)) return { kind: "missing" };
|
|
41
|
+
throw new Error("P2P remote versioninfo could not be read.");
|
|
42
|
+
}
|
|
43
|
+
function resolvePathIdentity(result, configuredVersion) {
|
|
44
|
+
if (result.kind === "missing") {
|
|
45
|
+
if (configuredVersion === 0) return 0;
|
|
46
|
+
throw new Error("P2P NFC database is missing path identity version information.");
|
|
47
|
+
}
|
|
48
|
+
const assessment = assessRemoteFeatureDocument(result.document);
|
|
49
|
+
if (assessment.status === "older-generation" && configuredVersion === 0) return 0;
|
|
50
|
+
if (assessment.status !== "supported") {
|
|
51
|
+
throw new Error(describeRemoteFeatureRejection(assessment));
|
|
52
|
+
}
|
|
53
|
+
return assessment.usedFeatures.includes(NFC_PATH_IDENTITY_FEATURE) ? 1 : 0;
|
|
54
|
+
}
|
|
55
|
+
async function readLocalP2PPathIdentity(database, configuredVersion) {
|
|
56
|
+
let result;
|
|
57
|
+
try {
|
|
58
|
+
result = { kind: "found", document: await database.get(VERSIONING_DOCID) };
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (!isErrorOfMissingDoc(error)) throw error;
|
|
61
|
+
result = { kind: "missing" };
|
|
62
|
+
}
|
|
63
|
+
return resolvePathIdentity(result, configuredVersion);
|
|
64
|
+
}
|
|
65
|
+
async function readRemoteP2PPathIdentity(database, configuredVersion) {
|
|
66
|
+
const response = await database.bulkGet({ docs: [{ id: VERSIONING_DOCID }] });
|
|
67
|
+
return resolvePathIdentity(parseRemotePathIdentityDocument(response), configuredVersion);
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
readLocalP2PPathIdentity,
|
|
71
|
+
readRemoteP2PPathIdentity
|
|
72
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parsePeerPatterns } from "./peerPatterns.js";
|
|
1
2
|
import { selfId, joinRoom } from "@trystero-p2p/nostr";
|
|
2
3
|
import { AutoAccepting, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE } from "../../common/types.js";
|
|
3
4
|
import { LOG_LEVEL_VERBOSE, Logger } from "../../common/logger.js";
|
|
@@ -281,12 +282,12 @@ You can chose as follows:
|
|
|
281
282
|
}
|
|
282
283
|
_acceptablePeers = new Computed({
|
|
283
284
|
evaluation: (settings) => {
|
|
284
|
-
return `${settings?.P2P_AutoAcceptingPeers ?? ""}
|
|
285
|
+
return parsePeerPatterns(`${settings?.P2P_AutoAcceptingPeers ?? ""}`);
|
|
285
286
|
}
|
|
286
287
|
});
|
|
287
288
|
_shouldDenyPeers = new Computed({
|
|
288
289
|
evaluation: (settings) => {
|
|
289
|
-
return `${settings?.P2P_AutoDenyingPeers ?? ""}
|
|
290
|
+
return parsePeerPatterns(`${settings?.P2P_AutoDenyingPeers ?? ""}`);
|
|
290
291
|
}
|
|
291
292
|
});
|
|
292
293
|
/**
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
splitP2PRelayUrls,
|
|
8
8
|
splitP2PTurnServerUrls
|
|
9
9
|
} from "../../common/models/setting.p2p.js";
|
|
10
|
+
import { getPathNormalizationVersion } from "../../common/pathNormalization.js";
|
|
10
11
|
function getP2PReplicatorConfigurationIdentity(settings) {
|
|
11
12
|
const managedTurnSelected = hasManagedP2PTurnConfiguration(settings);
|
|
12
13
|
const turnServerValue = settings.P2P_turnServers ?? "";
|
|
@@ -25,6 +26,7 @@ function getP2PReplicatorConfigurationIdentity(settings) {
|
|
|
25
26
|
managedTurnSelected ? [settings.P2P_managedType, settings.P2P_managedId, settings.P2P_managedToken] : void 0,
|
|
26
27
|
normaliseP2PMaxWirePayloadBytes(settings.P2P_maxWirePayloadBytes),
|
|
27
28
|
effectivePath,
|
|
29
|
+
getPathNormalizationVersion(settings),
|
|
28
30
|
settings.P2P_useDiagRTC ?? false
|
|
29
31
|
]);
|
|
30
32
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parsePeerPatterns(value: string): RegExp[];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
function parsePeerPatterns(value) {
|
|
2
|
+
return value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0).map((entry) => entry.startsWith("~") ? new RegExp(entry.substring(1), "i") : new RegExp(`^${entry}$`, "i"));
|
|
3
|
+
}
|
|
4
|
+
export {
|
|
5
|
+
parsePeerPatterns
|
|
6
|
+
};
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
supportedStopActiveTransfer
|
|
16
16
|
} from "../index.js";
|
|
17
17
|
import { getP2PReplicatorConfigurationIdentity } from "./p2pReplicatorConfigurationIdentity.js";
|
|
18
|
-
import {
|
|
18
|
+
import { assertSupportedPathNormalizationVersion } from "../../common/pathNormalization.js";
|
|
19
19
|
function useP2PReplicatorFeature(host, openReplicationUIFactory, openRebuildUIFactory, options = {}) {
|
|
20
20
|
const service = createP2PService(
|
|
21
21
|
{
|
|
@@ -63,7 +63,11 @@ function useP2PReplicatorFeature(host, openReplicationUIFactory, openRebuildUIFa
|
|
|
63
63
|
kind: REMOTE_P2P,
|
|
64
64
|
diagnosticName: "P2P",
|
|
65
65
|
readiness: PEER_REPLICATION_READINESS,
|
|
66
|
-
isConfigured: (settings) =>
|
|
66
|
+
isConfigured: (settings) => {
|
|
67
|
+
if (settings.remoteType !== REMOTE_P2P || !settings.P2P_Enabled) return false;
|
|
68
|
+
assertSupportedPathNormalizationVersion(settings.pathNormalizationVersion);
|
|
69
|
+
return true;
|
|
70
|
+
},
|
|
67
71
|
configurationIdentity: getP2PReplicatorConfigurationIdentity,
|
|
68
72
|
create: createP2PReplicator,
|
|
69
73
|
remoteResources: NO_REMOTE_RESOURCE_CAPABILITIES,
|
package/dist/rpc/RpcRoom.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare class RpcRoom {
|
|
|
8
8
|
private sessions;
|
|
9
9
|
private outgoingChunkMap;
|
|
10
10
|
private incomingChunkMap;
|
|
11
|
-
private
|
|
11
|
+
private incomingChunkWaits;
|
|
12
12
|
private peerVersion;
|
|
13
13
|
private disposers;
|
|
14
14
|
private closed;
|
|
@@ -32,7 +32,11 @@ export declare class RpcRoom {
|
|
|
32
32
|
private notifyRemoteCancellation;
|
|
33
33
|
private abortInboundCall;
|
|
34
34
|
private sendEnvelope;
|
|
35
|
+
private sendChunk;
|
|
36
|
+
private sendErrorResponse;
|
|
35
37
|
private scheduleMissingAck;
|
|
36
38
|
private onWireMessage;
|
|
37
39
|
private onEnvelopePayload;
|
|
40
|
+
private onRequest;
|
|
41
|
+
private runMethod;
|
|
38
42
|
}
|