@vrtmrz/livesync-commonlib 0.2.3 → 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 +33 -8
- 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/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/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/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 +13 -10
- package/dist/pouchdb/pathIdentityCompatibility.d.ts +8 -0
- package/dist/pouchdb/pathIdentityCompatibility.js +19 -1
- 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 +35 -19
- 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 +98 -70
- package/dist/replication/trystero/TrysteroReplicator.pathIdentity.d.ts +11 -0
- package/dist/replication/trystero/TrysteroReplicator.pathIdentity.js +72 -0
- package/dist/replication/trystero/p2pReplicatorConfigurationIdentity.js +2 -0
- package/dist/replication/trystero/useP2PReplicatorFeature.js +6 -2
- package/dist/serviceModules/Rebuilder.d.ts +1 -0
- package/dist/serviceModules/Rebuilder.js +33 -5
- package/dist/worker/bgWorker.js +1 -1
- package/docs/conflict-resolution.md +1 -1
- package/docs/development.md +4 -0
- package/docs/proven-in-use.md +30 -13
- package/docs/releasing.md +2 -0
- package/docs/service-feature-composition.md +7 -1
- package/docs/unicode-path-identity.md +64 -3
- package/package.json +2 -2
|
@@ -12,7 +12,7 @@ import { MinioStorageAdapter } from "./objectstore/MinioStorageAdapter.js";
|
|
|
12
12
|
import { LiveSyncAbstractReplicator } from "../LiveSyncAbstractReplicator.js";
|
|
13
13
|
import { ensureRemoteIsCompatible } from "../../pouchdb/LiveSyncDBFunctions.js";
|
|
14
14
|
import { getEffectiveTweakValues } from "../../pouchdb/LiveSyncDBFunctions.js";
|
|
15
|
-
import { getPathNormalizationVersion } from "../../common/pathNormalization.js";
|
|
15
|
+
import { assertSupportedPathNormalizationVersion, getPathNormalizationVersion } from "../../common/pathNormalization.js";
|
|
16
16
|
import { JournalStorageReadStatuses } from "./objectstore/JournalStorageAdapter.js";
|
|
17
17
|
import { computeKeyedId, configuredIdKey } from "../../common/idDerivation.js";
|
|
18
18
|
import { decryptString, encryptString } from "../../encryption/stringEncryption.js";
|
|
@@ -44,8 +44,17 @@ const currentVersionRange = {
|
|
|
44
44
|
current: 2
|
|
45
45
|
};
|
|
46
46
|
function journalVersionRange(setting) {
|
|
47
|
+
if (getPathNormalizationVersion(setting) === 1) return { min: 4, max: 4, current: 4 };
|
|
47
48
|
return configuredIdKey(setting) && setting.encrypt && setting.usePathObfuscation ? { min: 3, max: 3, current: 3 } : currentVersionRange;
|
|
48
49
|
}
|
|
50
|
+
function pathIdentityFromJournalRange(range) {
|
|
51
|
+
if (range?.min === 4 && range?.max === 4) return 1;
|
|
52
|
+
if (!range) return 0;
|
|
53
|
+
if (Number.isSafeInteger(range.min) && Number.isSafeInteger(range.max) && range.min >= 0 && range.max <= 3 && range.min <= range.max) {
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
56
|
+
throw new Error("The Journal declares an unsupported path identity range.");
|
|
57
|
+
}
|
|
49
58
|
class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
|
|
50
59
|
get client() {
|
|
51
60
|
return this.setupJournalSyncClient();
|
|
@@ -66,7 +75,7 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
|
|
|
66
75
|
return await this.setupJournalSyncClient(setting).getReplicationPBKDF2Salt(refresh);
|
|
67
76
|
}
|
|
68
77
|
setupJournalSyncClient(settings = this.currentSettings) {
|
|
69
|
-
|
|
78
|
+
assertSupportedPathNormalizationVersion(settings.pathNormalizationVersion);
|
|
70
79
|
if (this._client) {
|
|
71
80
|
this._client.applyNewConfig(settings, this.simpleStore, this.env);
|
|
72
81
|
} else {
|
|
@@ -79,33 +88,105 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
|
|
|
79
88
|
}
|
|
80
89
|
return this._client;
|
|
81
90
|
}
|
|
82
|
-
async
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
async isBucketPathIdentityCompatible(setting, milestone, client) {
|
|
92
|
+
const pathVersion = getPathNormalizationVersion(setting);
|
|
93
|
+
const ranges = milestone?.node_chunk_info ?? {};
|
|
94
|
+
const nodes = /* @__PURE__ */ new Set([...milestone?.accepted_nodes ?? [], ...Object.keys(ranges)]);
|
|
95
|
+
for (const node of nodes) {
|
|
96
|
+
const range = ranges[node];
|
|
97
|
+
const usesNFC = range?.min === 4 && range?.max === 4;
|
|
98
|
+
if (usesNFC !== (pathVersion === 1)) return false;
|
|
99
|
+
}
|
|
100
|
+
const preferred = milestone?.tweak_values?.[DEVICE_ID_PREFERRED];
|
|
101
|
+
if (preferred && getPathNormalizationVersion(preferred) !== pathVersion) return false;
|
|
102
|
+
if (nodes.size === 0 && await client.hasRemoteJournals()) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
"The Journal compatibility milestone is missing. Rebuild from the authoritative Vault before synchronising."
|
|
105
|
+
);
|
|
86
106
|
}
|
|
87
|
-
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
async isBucketIdDerivationCompatible(setting, milestone) {
|
|
88
110
|
const idKey = configuredIdKey(setting);
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
111
|
+
const usesKeyedDocumentIds = !!idKey && setting.encrypt && setting.usePathObfuscation;
|
|
112
|
+
const preferred = milestone?.tweak_values?.[DEVICE_ID_PREFERRED];
|
|
113
|
+
const stored = milestone?.encrypted_id_derivation_proof;
|
|
114
|
+
if (!usesKeyedDocumentIds) {
|
|
115
|
+
return !stored && !(preferred?.idDerivationVersion === 1 && preferred.usePathObfuscation === true);
|
|
93
116
|
}
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
117
|
+
if (!milestone) return true;
|
|
118
|
+
if (!stored) return false;
|
|
119
|
+
const expected = await computeKeyedId(idKey, "remote-agreement", "journal-milestone-v1");
|
|
120
|
+
try {
|
|
121
|
+
return await decryptString(stored, setting.passphrase) === expected;
|
|
122
|
+
} catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async assertBucketIdentity(setting, milestone, client) {
|
|
127
|
+
if (!await this.isBucketPathIdentityCompatible(setting, milestone, client)) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
"The Journal and settings use different path identity versions. Rebuild from the authoritative Vault or Fetch with matching settings before synchronising."
|
|
130
|
+
);
|
|
108
131
|
}
|
|
132
|
+
if (!await this.isBucketIdDerivationCompatible(setting, milestone)) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
"The Journal and settings use different document ID derivation modes or keys. Rebuild from the authoritative Vault or Fetch with matching settings before synchronising."
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** Inspect identity without publishing this node, preparing a seed, or changing checkpoints. */
|
|
139
|
+
async assertRemotePathIdentity(setting) {
|
|
140
|
+
const client = this.setupJournalSyncClient(setting);
|
|
141
|
+
await this.assertBucketIdentity(setting, await readRemoteMilestone(client), client);
|
|
142
|
+
}
|
|
143
|
+
/** Read path identity for initial setup without publishing a node or preparing a seed. */
|
|
144
|
+
async inspectRemotePathIdentity(setting) {
|
|
145
|
+
const client = this.setupJournalSyncClient(setting);
|
|
146
|
+
const milestone = await readRemoteMilestone(client);
|
|
147
|
+
const versions = /* @__PURE__ */ new Set();
|
|
148
|
+
const ranges = milestone?.node_chunk_info ?? {};
|
|
149
|
+
const nodes = /* @__PURE__ */ new Set([...milestone?.accepted_nodes ?? [], ...Object.keys(ranges)]);
|
|
150
|
+
for (const node of nodes) {
|
|
151
|
+
versions.add(pathIdentityFromJournalRange(ranges[node]));
|
|
152
|
+
}
|
|
153
|
+
const preferred = milestone?.tweak_values?.[DEVICE_ID_PREFERRED];
|
|
154
|
+
if (preferred) versions.add(getPathNormalizationVersion(preferred));
|
|
155
|
+
if (versions.size > 1) throw new Error("The Journal has conflicting path identity declarations.");
|
|
156
|
+
if (nodes.size === 0 && await client.hasRemoteJournals()) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
"The Journal path identity declaration is missing. Use matching settings or Rebuild from the authoritative Vault."
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
const version = [...versions][0];
|
|
162
|
+
return version === void 0 ? { status: "empty" } : { status: "configured", pathNormalizationVersion: version };
|
|
163
|
+
}
|
|
164
|
+
/** Lock existing journals for replacement without declaring the replacement's identity. */
|
|
165
|
+
async markRemoteLockedForRebuild(setting) {
|
|
166
|
+
assertSupportedPathNormalizationVersion(setting.pathNormalizationVersion);
|
|
167
|
+
const client = this.setupJournalSyncClient(setting);
|
|
168
|
+
const milestone = await readRemoteMilestone(client);
|
|
169
|
+
if (!milestone) return;
|
|
170
|
+
const previousRange = Object.values(milestone.node_chunk_info ?? {}).sort((a, b) => b.min - a.min)[0];
|
|
171
|
+
const lockedMilestone = {
|
|
172
|
+
...milestone,
|
|
173
|
+
locked: true,
|
|
174
|
+
accepted_nodes: previousRange ? [this.nodeid] : [],
|
|
175
|
+
node_chunk_info: previousRange ? { ...milestone.node_chunk_info, [this.nodeid]: { ...previousRange } } : milestone.node_chunk_info
|
|
176
|
+
};
|
|
177
|
+
if (!await client.uploadJson(MILSTONE_DOCID, lockedMilestone)) {
|
|
178
|
+
throw new Error("Could not upload remote milestone");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async ensureBucketIsCompatible(deviceNodeID, currentVersionRange2, setting = this.currentSettings, client = this.setupJournalSyncClient(setting), recordTweakAssessment) {
|
|
182
|
+
const downloadedMilestone = await readRemoteMilestone(client) ?? false;
|
|
183
|
+
if (!await this.isBucketPathIdentityCompatible(setting, downloadedMilestone || void 0, client)) {
|
|
184
|
+
return "INCOMPATIBLE";
|
|
185
|
+
}
|
|
186
|
+
if (!await this.isBucketIdDerivationCompatible(setting, downloadedMilestone || void 0)) {
|
|
187
|
+
return "ID_KEY_MISMATCH";
|
|
188
|
+
}
|
|
189
|
+
const encryptedProof = downloadedMilestone ? void 0 : await this.createInitialIdDerivationProof(setting);
|
|
109
190
|
const cPointInfo = await client.getCheckpointInfo();
|
|
110
191
|
const progress = [...cPointInfo?.receivedFiles || []].sort().pop() || "";
|
|
111
192
|
return await ensureRemoteIsCompatible(
|
|
@@ -291,7 +372,7 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
|
|
|
291
372
|
}
|
|
292
373
|
async checkReplicationConnectivity(skipCheck, ignoreCleanLock = false, showMessage = false, setting = this.currentSettings, client, recordCompatibilityDecision) {
|
|
293
374
|
recordCompatibilityDecision?.(CENTRAL_COMPATIBILITY_NOT_ASSESSED);
|
|
294
|
-
|
|
375
|
+
assertSupportedPathNormalizationVersion(setting.pathNormalizationVersion);
|
|
295
376
|
const activeClient = client ?? this.setupJournalSyncClient(setting);
|
|
296
377
|
if (!await activeClient.isAvailable()) {
|
|
297
378
|
return false;
|
|
@@ -388,7 +469,7 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
|
|
|
388
469
|
this.updateInfo();
|
|
389
470
|
}
|
|
390
471
|
async tryResetRemoteDatabase(setting) {
|
|
391
|
-
|
|
472
|
+
assertSupportedPathNormalizationVersion(setting.pathNormalizationVersion);
|
|
392
473
|
this.closeReplication();
|
|
393
474
|
try {
|
|
394
475
|
if (!await this.client.resetBucket()) {
|
|
@@ -403,7 +484,7 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
|
|
|
403
484
|
}
|
|
404
485
|
}
|
|
405
486
|
async tryCreateRemoteDatabase(setting) {
|
|
406
|
-
|
|
487
|
+
assertSupportedPathNormalizationVersion(setting.pathNormalizationVersion);
|
|
407
488
|
this.closeReplication();
|
|
408
489
|
Logger("Remote Database Created or Connected", LOG_LEVEL_NOTICE);
|
|
409
490
|
if (!await this.ensurePBKDF2Salt(setting, true, false)) {
|
|
@@ -418,7 +499,7 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
|
|
|
418
499
|
return await encryptString(proof, setting.passphrase);
|
|
419
500
|
}
|
|
420
501
|
async markRemoteLocked(setting, locked, lockByClean) {
|
|
421
|
-
|
|
502
|
+
assertSupportedPathNormalizationVersion(setting.pathNormalizationVersion);
|
|
422
503
|
const defInitPoint = {
|
|
423
504
|
_id: MILSTONE_DOCID,
|
|
424
505
|
type: "milestoneinfo",
|
|
@@ -432,6 +513,7 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
|
|
|
432
513
|
};
|
|
433
514
|
const client = this.setupJournalSyncClient(setting);
|
|
434
515
|
const existingMilestone = await readRemoteMilestone(client);
|
|
516
|
+
await this.assertBucketIdentity(setting, existingMilestone, client);
|
|
435
517
|
const initialProof = existingMilestone ? void 0 : await this.createInitialIdDerivationProof(setting);
|
|
436
518
|
const remoteMilestone = {
|
|
437
519
|
...defInitPoint,
|
|
@@ -452,7 +534,7 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
|
|
|
452
534
|
}
|
|
453
535
|
}
|
|
454
536
|
async markRemoteResolved(setting) {
|
|
455
|
-
|
|
537
|
+
assertSupportedPathNormalizationVersion(setting.pathNormalizationVersion);
|
|
456
538
|
const defInitPoint = {
|
|
457
539
|
_id: MILSTONE_DOCID,
|
|
458
540
|
type: "milestoneinfo",
|
|
@@ -465,6 +547,7 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
|
|
|
465
547
|
};
|
|
466
548
|
const client = this.setupJournalSyncClient(setting);
|
|
467
549
|
const existingMilestone = await readRemoteMilestone(client);
|
|
550
|
+
await this.assertBucketIdentity(setting, existingMilestone, client);
|
|
468
551
|
const initialProof = existingMilestone ? void 0 : await this.createInitialIdDerivationProof(setting);
|
|
469
552
|
const remoteMilestone = {
|
|
470
553
|
...defInitPoint,
|
|
@@ -482,7 +565,7 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
|
|
|
482
565
|
const endpoint = setting.endpoint;
|
|
483
566
|
let testClient;
|
|
484
567
|
try {
|
|
485
|
-
|
|
568
|
+
assertSupportedPathNormalizationVersion(setting.pathNormalizationVersion);
|
|
486
569
|
testClient = new MinioStorageAdapter(setting, this.env);
|
|
487
570
|
await testClient.listFiles("", 1);
|
|
488
571
|
Logger(`Connected to ${endpoint} successfully!`, LOG_LEVEL_NOTICE);
|
|
@@ -497,12 +580,13 @@ ${ex.message}`, LOG_LEVEL_NOTICE);
|
|
|
497
580
|
}
|
|
498
581
|
}
|
|
499
582
|
async resetRemoteTweakSettings(setting) {
|
|
500
|
-
|
|
583
|
+
assertSupportedPathNormalizationVersion(setting.pathNormalizationVersion);
|
|
501
584
|
try {
|
|
502
585
|
const remoteMilestone = await this.client.downloadJson(MILSTONE_DOCID);
|
|
503
586
|
if (!remoteMilestone) {
|
|
504
587
|
throw new Error("Missing remote milestone");
|
|
505
588
|
}
|
|
589
|
+
await this.assertBucketIdentity(setting, remoteMilestone, this.client);
|
|
506
590
|
remoteMilestone.tweak_values = {};
|
|
507
591
|
Logger(`tweak values on the remote database have been cleared`, LOG_LEVEL_VERBOSE);
|
|
508
592
|
await this.client.uploadJson(MILSTONE_DOCID, remoteMilestone);
|
|
@@ -512,7 +596,7 @@ ${ex.message}`, LOG_LEVEL_NOTICE);
|
|
|
512
596
|
}
|
|
513
597
|
}
|
|
514
598
|
async setPreferredRemoteTweakSettings(setting) {
|
|
515
|
-
|
|
599
|
+
assertSupportedPathNormalizationVersion(setting.pathNormalizationVersion);
|
|
516
600
|
const trialClient = new JournalSyncCore(
|
|
517
601
|
setting,
|
|
518
602
|
this.simpleStore,
|
|
@@ -524,6 +608,7 @@ ${ex.message}`, LOG_LEVEL_NOTICE);
|
|
|
524
608
|
if (!remoteMilestone) {
|
|
525
609
|
throw new Error("Missing remote milestone");
|
|
526
610
|
}
|
|
611
|
+
await this.assertBucketIdentity(setting, remoteMilestone, trialClient);
|
|
527
612
|
remoteMilestone.tweak_values[DEVICE_ID_PREFERRED] = getEffectiveTweakValues(setting);
|
|
528
613
|
Logger(`Preferred tweak values have been registered`, LOG_LEVEL_VERBOSE);
|
|
529
614
|
if (!await trialClient.uploadJson(MILSTONE_DOCID, remoteMilestone)) {
|
|
@@ -537,7 +622,7 @@ ${ex.message}`, LOG_LEVEL_NOTICE);
|
|
|
537
622
|
}
|
|
538
623
|
}
|
|
539
624
|
async getRemotePreferredTweakValues(setting) {
|
|
540
|
-
|
|
625
|
+
assertSupportedPathNormalizationVersion(setting.pathNormalizationVersion);
|
|
541
626
|
const trialClient = new JournalSyncCore(
|
|
542
627
|
setting,
|
|
543
628
|
this.simpleStore,
|
|
@@ -574,7 +659,7 @@ ${ex.message}`, LOG_LEVEL_NOTICE);
|
|
|
574
659
|
}
|
|
575
660
|
}
|
|
576
661
|
async getRemoteStatus(setting) {
|
|
577
|
-
|
|
662
|
+
assertSupportedPathNormalizationVersion(setting.pathNormalizationVersion);
|
|
578
663
|
const testClient = new MinioStorageAdapter(setting, this.env);
|
|
579
664
|
try {
|
|
580
665
|
return await testClient.getUsage();
|
|
@@ -582,14 +667,6 @@ ${ex.message}`, LOG_LEVEL_NOTICE);
|
|
|
582
667
|
testClient.dispose();
|
|
583
668
|
}
|
|
584
669
|
}
|
|
585
|
-
supportsLegacyPathIdentity(setting) {
|
|
586
|
-
return getPathNormalizationVersion(setting) === 0;
|
|
587
|
-
}
|
|
588
|
-
assertLegacyPathIdentity(setting) {
|
|
589
|
-
if (!this.supportsLegacyPathIdentity(setting)) {
|
|
590
|
-
throw new Error("Object Storage does not support NFC path identity.");
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
670
|
async getOwnJoinedDeviceId() {
|
|
594
671
|
const nodeInfo = await this.rawDatabase.get(NODEINFO_DOCID);
|
|
595
672
|
const nodeId = nodeInfo?.nodeid;
|
|
@@ -599,7 +676,7 @@ ${ex.message}`, LOG_LEVEL_NOTICE);
|
|
|
599
676
|
return nodeId;
|
|
600
677
|
}
|
|
601
678
|
async getJoinedDevices(setting) {
|
|
602
|
-
|
|
679
|
+
assertSupportedPathNormalizationVersion(setting.pathNormalizationVersion);
|
|
603
680
|
const trialClient = new JournalSyncCore(
|
|
604
681
|
setting,
|
|
605
682
|
this.simpleStore,
|
|
@@ -616,7 +693,7 @@ ${ex.message}`, LOG_LEVEL_NOTICE);
|
|
|
616
693
|
}
|
|
617
694
|
}
|
|
618
695
|
async forgetJoinedDevice(setting, deviceId) {
|
|
619
|
-
|
|
696
|
+
assertSupportedPathNormalizationVersion(setting.pathNormalizationVersion);
|
|
620
697
|
const ownDeviceId = await this.getOwnJoinedDeviceId();
|
|
621
698
|
const trialClient = new JournalSyncCore(
|
|
622
699
|
setting,
|
|
@@ -630,6 +707,7 @@ ${ex.message}`, LOG_LEVEL_NOTICE);
|
|
|
630
707
|
const result = await trialClient.downloadJsonWithResult(MILSTONE_DOCID);
|
|
631
708
|
if (result.status === JournalStorageReadStatuses.NOT_FOUND) return void 0;
|
|
632
709
|
if (result.status === JournalStorageReadStatuses.UNAVAILABLE) throw result.error;
|
|
710
|
+
await this.assertBucketIdentity(setting, result.value, trialClient);
|
|
633
711
|
return { milestone: result.value, revision: result.revision };
|
|
634
712
|
},
|
|
635
713
|
write: async (milestone, revision) => await trialClient.uploadJsonIfUnchanged(MILSTONE_DOCID, milestone, revision)
|
|
@@ -12,7 +12,7 @@ import { P2PRoomSession } from "./P2PRoomSession.js";
|
|
|
12
12
|
import { P2PAutomationCoordinator } from "./P2PAutomationCoordinator.js";
|
|
13
13
|
import { PEER_REPLICATION_READINESS } from "../ReplicatorProvider.js";
|
|
14
14
|
import { getP2PReplicatorConfigurationIdentity } from "./p2pReplicatorConfigurationIdentity.js";
|
|
15
|
-
import { getPathNormalizationVersion } from "../../common/pathNormalization.js";
|
|
15
|
+
import { assertSupportedPathNormalizationVersion, getPathNormalizationVersion } from "../../common/pathNormalization.js";
|
|
16
16
|
import {
|
|
17
17
|
ACTIVE_P2P_RELAY_BINDING_CONFLICT,
|
|
18
18
|
activeP2PRelayBindingCovers
|
|
@@ -58,8 +58,8 @@ class P2PRoomSessionOwner {
|
|
|
58
58
|
/** Reconcile one long-lived reason for keeping the room available. */
|
|
59
59
|
async setPersistentDemand(demand, active) {
|
|
60
60
|
const settings = this.env.services.setting.currentSettings();
|
|
61
|
-
|
|
62
|
-
const enabled = settings.P2P_Enabled
|
|
61
|
+
assertSupportedPathNormalizationVersion(settings.pathNormalizationVersion);
|
|
62
|
+
const enabled = settings.P2P_Enabled;
|
|
63
63
|
if (active && enabled) {
|
|
64
64
|
this.persistentDemands.add(demand);
|
|
65
65
|
} else {
|
|
@@ -67,8 +67,6 @@ class P2PRoomSessionOwner {
|
|
|
67
67
|
}
|
|
68
68
|
if (active && !settings.P2P_Enabled) {
|
|
69
69
|
Logger(this.env.services.context.translate("P2P.NotEnabled"), LOG_LEVEL_NOTICE);
|
|
70
|
-
} else if (active && !supportsPathIdentity) {
|
|
71
|
-
Logger("P2P does not support NFC path identity.", LOG_LEVEL_NOTICE);
|
|
72
70
|
}
|
|
73
71
|
await this.fencePreparationForCurrentSettings(enabled);
|
|
74
72
|
await this.reconcileTransport();
|
|
@@ -81,12 +79,10 @@ class P2PRoomSessionOwner {
|
|
|
81
79
|
*/
|
|
82
80
|
async runWithFiniteDemand(task) {
|
|
83
81
|
const settings = this.env.services.setting.currentSettings();
|
|
82
|
+
assertSupportedPathNormalizationVersion(settings.pathNormalizationVersion);
|
|
84
83
|
if (!settings.P2P_Enabled) {
|
|
85
84
|
throw new Error("P2P is not enabled.");
|
|
86
85
|
}
|
|
87
|
-
if (getPathNormalizationVersion(settings) !== 0) {
|
|
88
|
-
throw new Error("P2P does not support NFC path identity.");
|
|
89
|
-
}
|
|
90
86
|
const demand = /* @__PURE__ */ Symbol("p2p-finite-room-demand");
|
|
91
87
|
this.finiteDemands.add(demand);
|
|
92
88
|
try {
|
|
@@ -108,8 +104,10 @@ class P2PRoomSessionOwner {
|
|
|
108
104
|
}
|
|
109
105
|
/** Arbitrate a complete signalling probe on the room lifecycle queue. */
|
|
110
106
|
runConnectionProbe(trialSettings, runOwnedTrial) {
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
try {
|
|
108
|
+
assertSupportedPathNormalizationVersion(this.env.services.setting.currentSettings().pathNormalizationVersion);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
return Promise.reject(error);
|
|
113
111
|
}
|
|
114
112
|
return this.enqueueLifecycleOperation(async () => {
|
|
115
113
|
const current = this.current;
|
|
@@ -135,7 +133,8 @@ class P2PRoomSessionOwner {
|
|
|
135
133
|
async reconcileTransport() {
|
|
136
134
|
await this.enqueueLifecycleOperation(async () => {
|
|
137
135
|
const settings = this.env.services.setting.currentSettings();
|
|
138
|
-
|
|
136
|
+
assertSupportedPathNormalizationVersion(settings.pathNormalizationVersion);
|
|
137
|
+
if (!settings.P2P_Enabled) {
|
|
139
138
|
this.persistentDemands.clear();
|
|
140
139
|
this.finiteDemands.clear();
|
|
141
140
|
}
|
|
@@ -390,17 +389,19 @@ class P2PRoomSessionOwner {
|
|
|
390
389
|
...this.env.services.setting.currentSettings()
|
|
391
390
|
});
|
|
392
391
|
const database = this.env.services.database.localDatabase.localDatabase;
|
|
392
|
+
const pathNormalizationVersion = getPathNormalizationVersion(settings);
|
|
393
393
|
this.automationCoordinator.reconcileIdentity(
|
|
394
394
|
JSON.stringify([
|
|
395
395
|
settings.P2P_AppID || "self-hosted-livesync",
|
|
396
396
|
settings.P2P_roomID,
|
|
397
|
-
settings.P2P_passphrase
|
|
397
|
+
settings.P2P_passphrase,
|
|
398
|
+
pathNormalizationVersion
|
|
398
399
|
]),
|
|
399
400
|
database
|
|
400
401
|
);
|
|
401
402
|
return {
|
|
402
403
|
database,
|
|
403
|
-
enabled: settings.P2P_Enabled
|
|
404
|
+
enabled: settings.P2P_Enabled,
|
|
404
405
|
settings,
|
|
405
406
|
deviceName,
|
|
406
407
|
signature: JSON.stringify([getP2PReplicatorConfigurationIdentity(settings), deviceName])
|
|
@@ -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>;
|