@vrtmrz/livesync-commonlib 0.1.16 → 0.1.18
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/dist/common/ConnectionString.js +9 -1
- package/dist/common/models/setting.const.d.ts +18 -0
- package/dist/common/models/setting.const.defaults.js +10 -1
- package/dist/common/models/setting.const.js +12 -0
- package/dist/common/models/setting.const.qr.js +3 -1
- package/dist/common/models/setting.p2p.d.ts +21 -0
- package/dist/common/models/setting.p2p.js +35 -0
- package/dist/common/models/setting.type.d.ts +11 -1
- package/dist/common/models/shared.definition.configNames.js +8 -0
- package/dist/common/models/tweak.definition.d.ts +2 -0
- package/dist/common/settingConstants.d.ts +2 -0
- package/dist/common/types.d.ts +5 -3
- package/dist/common/types.js +17 -1
- package/dist/common/utils.js +4 -1
- package/dist/interfaces/DatabaseFileAccess.d.ts +10 -0
- package/dist/managers/EntryManager/EntryManager.d.ts +1 -0
- package/dist/managers/EntryManager/EntryManager.js +4 -0
- package/dist/managers/EntryManager/EntryManagerImpls.d.ts +7 -0
- package/dist/managers/EntryManager/EntryManagerImpls.js +21 -3
- package/dist/pouchdb/LiveSyncLocalDB.d.ts +1 -0
- package/dist/pouchdb/LiveSyncLocalDB.js +3 -0
- package/dist/replication/trystero/LiveSyncTrysteroReplicator.d.ts +8 -0
- package/dist/replication/trystero/LiveSyncTrysteroReplicator.js +36 -8
- package/dist/replication/trystero/TrysteroReplicatorP2PServer.js +2 -2
- package/dist/rpc/transports/TrysteroTransport.d.ts +2 -0
- package/dist/rpc/transports/TrysteroTransport.js +18 -2
- package/dist/rpc/transports/trysteroUtils.js +12 -1
- package/dist/serviceModules/ServiceDatabaseFileAccessBase.d.ts +1 -0
- package/dist/serviceModules/ServiceDatabaseFileAccessBase.js +22 -4
- package/dist/worker/bgWorker.js +1 -1
- package/docs/conflict-resolution.md +2 -0
- package/docs/development.md +1 -1
- package/docs/p2p-transport-lifecycle.md +4 -2
- package/docs/proven-in-use.md +3 -1
- package/docs/releasing.md +2 -0
- package/docs/remote-configurations.md +2 -0
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normaliseP2PConnectionPath, normaliseP2PMaxWirePayloadBytes } from "./models/setting.p2p.js";
|
|
1
2
|
const PROXY_SCHEME = "https";
|
|
2
3
|
function parseSlsUri(uriString) {
|
|
3
4
|
const match = uriString.match(/^sls\+([^:]+):(.*)$/);
|
|
@@ -155,7 +156,9 @@ class ConnectionStringParser {
|
|
|
155
156
|
P2P_AutoBroadcast: searchParams.get("autoBroadcast") === "true",
|
|
156
157
|
P2P_turnServers: searchParams.get("turnServers") || "",
|
|
157
158
|
P2P_turnUsername: searchParams.get("turnUser") || "",
|
|
158
|
-
P2P_turnCredential: searchParams.get("turnPass") || ""
|
|
159
|
+
P2P_turnCredential: searchParams.get("turnPass") || "",
|
|
160
|
+
P2P_maxWirePayloadBytes: normaliseP2PMaxWirePayloadBytes(Number(searchParams.get("maxWirePayloadBytes"))),
|
|
161
|
+
P2P_connectionPath: normaliseP2PConnectionPath(searchParams.get("connectionPath"))
|
|
159
162
|
};
|
|
160
163
|
}
|
|
161
164
|
static serializeP2P(settings) {
|
|
@@ -168,6 +171,11 @@ class ConnectionStringParser {
|
|
|
168
171
|
if (settings.P2P_turnServers) searchParams.set("turnServers", settings.P2P_turnServers);
|
|
169
172
|
if (settings.P2P_turnUsername) searchParams.set("turnUser", settings.P2P_turnUsername);
|
|
170
173
|
if (settings.P2P_turnCredential) searchParams.set("turnPass", settings.P2P_turnCredential);
|
|
174
|
+
searchParams.set(
|
|
175
|
+
"maxWirePayloadBytes",
|
|
176
|
+
String(normaliseP2PMaxWirePayloadBytes(settings.P2P_maxWirePayloadBytes))
|
|
177
|
+
);
|
|
178
|
+
searchParams.set("connectionPath", normaliseP2PConnectionPath(settings.P2P_connectionPath));
|
|
171
179
|
const credentials = settings.P2P_passphrase ? `:${encodeURIComponent(settings.P2P_passphrase)}@` : "";
|
|
172
180
|
const host = encodeURIComponent(settings.P2P_roomID);
|
|
173
181
|
const query = searchParams.toString() ? `?${searchParams.toString()}` : "";
|
|
@@ -9,6 +9,24 @@ export declare const RemoteTypes: {
|
|
|
9
9
|
export declare const REMOTE_COUCHDB: "";
|
|
10
10
|
export declare const REMOTE_MINIO: "MINIO";
|
|
11
11
|
export declare const REMOTE_P2P: "ONLY_P2P";
|
|
12
|
+
/** ICE route policies supported by a P2P connection profile. */
|
|
13
|
+
export declare const P2PConnectionPaths: {
|
|
14
|
+
readonly Automatic: "automatic";
|
|
15
|
+
readonly Relay: "relay";
|
|
16
|
+
};
|
|
17
|
+
export type P2PConnectionPath = (typeof P2PConnectionPaths)[keyof typeof P2PConnectionPaths];
|
|
18
|
+
/**
|
|
19
|
+
* User-facing message-size presets for the P2P RPC transport.
|
|
20
|
+
*
|
|
21
|
+
* These values limit Commonlib wire payloads before Trystero applies its own
|
|
22
|
+
* framing. They are not file Chunk sizes or network MTUs.
|
|
23
|
+
*/
|
|
24
|
+
export declare const P2PMessageSizePresets: {
|
|
25
|
+
readonly Standard: 15360;
|
|
26
|
+
readonly Reduced: 2048;
|
|
27
|
+
readonly Conservative: 1024;
|
|
28
|
+
readonly MaximumCompatibility: 800;
|
|
29
|
+
};
|
|
12
30
|
export declare const E2EEAlgorithmNames: {
|
|
13
31
|
readonly "": "V1: Legacy";
|
|
14
32
|
readonly v2: "V2: AES-256-GCM With HKDF";
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ChunkAlgorithms,
|
|
3
|
+
CURRENT_SETTING_VERSION,
|
|
4
|
+
E2EEAlgorithms,
|
|
5
|
+
P2PConnectionPaths,
|
|
6
|
+
P2PMessageSizePresets,
|
|
7
|
+
REMOTE_COUCHDB
|
|
8
|
+
} from "./setting.const.js";
|
|
2
9
|
import { PREFERRED_BASE } from "./setting.const.preferred.js";
|
|
3
10
|
import { AutoAccepting } from "./setting.type.js";
|
|
4
11
|
const P2P_DEFAULT_SETTINGS = {
|
|
@@ -21,6 +28,8 @@ const P2P_DEFAULT_SETTINGS = {
|
|
|
21
28
|
P2P_turnServers: "",
|
|
22
29
|
P2P_turnUsername: "",
|
|
23
30
|
P2P_turnCredential: "",
|
|
31
|
+
P2P_maxWirePayloadBytes: P2PMessageSizePresets.Standard,
|
|
32
|
+
P2P_connectionPath: P2PConnectionPaths.Automatic,
|
|
24
33
|
P2P_useDiagRTC: false
|
|
25
34
|
};
|
|
26
35
|
const SETTINGS_SCHEMA_DEFAULTS = {
|
|
@@ -9,6 +9,16 @@ const RemoteTypes = {
|
|
|
9
9
|
const REMOTE_COUCHDB = RemoteTypes.REMOTE_COUCHDB;
|
|
10
10
|
const REMOTE_MINIO = RemoteTypes.REMOTE_MINIO;
|
|
11
11
|
const REMOTE_P2P = RemoteTypes.REMOTE_P2P;
|
|
12
|
+
const P2PConnectionPaths = {
|
|
13
|
+
Automatic: "automatic",
|
|
14
|
+
Relay: "relay"
|
|
15
|
+
};
|
|
16
|
+
const P2PMessageSizePresets = {
|
|
17
|
+
Standard: 15360,
|
|
18
|
+
Reduced: 2048,
|
|
19
|
+
Conservative: 1024,
|
|
20
|
+
MaximumCompatibility: 800
|
|
21
|
+
};
|
|
12
22
|
const E2EEAlgorithmNames = {
|
|
13
23
|
"": "V1: Legacy",
|
|
14
24
|
v2: "V2: AES-256-GCM With HKDF",
|
|
@@ -59,6 +69,8 @@ export {
|
|
|
59
69
|
MODE_SELECTIVE,
|
|
60
70
|
MODE_SHINY,
|
|
61
71
|
NetworkWarningStyles,
|
|
72
|
+
P2PConnectionPaths,
|
|
73
|
+
P2PMessageSizePresets,
|
|
62
74
|
REMOTE_COUCHDB,
|
|
63
75
|
REMOTE_MINIO,
|
|
64
76
|
REMOTE_P2P,
|
|
@@ -168,8 +168,10 @@ const KeyIndexOfSettings = {
|
|
|
168
168
|
P2P_ActiveRemoteConfigurationId: 159,
|
|
169
169
|
autoAcceptCompatibleTweak: 160,
|
|
170
170
|
tweakModified: -1,
|
|
171
|
-
P2P_useDiagRTC: -1
|
|
171
|
+
P2P_useDiagRTC: -1,
|
|
172
172
|
// Do not encode into the QR Code.
|
|
173
|
+
P2P_maxWirePayloadBytes: 163,
|
|
174
|
+
P2P_connectionPath: 164
|
|
173
175
|
};
|
|
174
176
|
export {
|
|
175
177
|
KeyIndexOfSettings
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type P2PConnectionPath } from "./setting.const.js";
|
|
2
|
+
/**
|
|
3
|
+
* Return a safe outgoing RPC wire-payload bound for Trystero.
|
|
4
|
+
*
|
|
5
|
+
* Existing profiles can omit this value. Invalid, excessively small, and
|
|
6
|
+
* excessively large values retain the established 15,360-byte behaviour.
|
|
7
|
+
*/
|
|
8
|
+
export declare function normaliseP2PMaxWirePayloadBytes(value: unknown): number;
|
|
9
|
+
/** Return a recognised P2P connection path, defaulting legacy values to automatic selection. */
|
|
10
|
+
export declare function normaliseP2PConnectionPath(value: unknown): P2PConnectionPath;
|
|
11
|
+
/** Split the stored comma-separated TURN server list without changing its entries. */
|
|
12
|
+
export declare function splitP2PTurnServerUrls(value: string): string[];
|
|
13
|
+
/**
|
|
14
|
+
* Report whether a value is a minimally valid TURN or TURN-over-TLS URL.
|
|
15
|
+
*
|
|
16
|
+
* Full allocation validation remains the responsibility of WebRTC. This
|
|
17
|
+
* check only prevents relay-only mode when no TURN endpoint can be attempted.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isValidP2PTurnServerUrl(value: string): boolean;
|
|
20
|
+
/** Report whether a stored TURN server list contains at least one usable TURN URL. */
|
|
21
|
+
export declare function hasValidP2PTurnServerUrl(value: string): boolean;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { P2PConnectionPaths, P2PMessageSizePresets } from "./setting.const.js";
|
|
2
|
+
function normaliseP2PMaxWirePayloadBytes(value) {
|
|
3
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < P2PMessageSizePresets.MaximumCompatibility || value > P2PMessageSizePresets.Standard) {
|
|
4
|
+
return P2PMessageSizePresets.Standard;
|
|
5
|
+
}
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
function normaliseP2PConnectionPath(value) {
|
|
9
|
+
return value === P2PConnectionPaths.Relay ? P2PConnectionPaths.Relay : P2PConnectionPaths.Automatic;
|
|
10
|
+
}
|
|
11
|
+
function splitP2PTurnServerUrls(value) {
|
|
12
|
+
return value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
13
|
+
}
|
|
14
|
+
function isValidP2PTurnServerUrl(value) {
|
|
15
|
+
const candidate = value.trim();
|
|
16
|
+
if (!candidate || /\s/.test(candidate)) return false;
|
|
17
|
+
try {
|
|
18
|
+
const parsed = new URL(candidate);
|
|
19
|
+
const protocol = parsed.protocol.toLowerCase();
|
|
20
|
+
if (protocol !== "turn:" && protocol !== "turns:") return false;
|
|
21
|
+
return (parsed.hostname || parsed.pathname).length > 0;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function hasValidP2PTurnServerUrl(value) {
|
|
27
|
+
return splitP2PTurnServerUrls(value).some(isValidP2PTurnServerUrl);
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
hasValidP2PTurnServerUrl,
|
|
31
|
+
isValidP2PTurnServerUrl,
|
|
32
|
+
normaliseP2PConnectionPath,
|
|
33
|
+
normaliseP2PMaxWirePayloadBytes,
|
|
34
|
+
splitP2PTurnServerUrls
|
|
35
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChunkAlgorithms, E2EEAlgorithms, HashAlgorithms, MODE_AUTOMATIC, MODE_PAUSED, MODE_SELECTIVE, MODE_SHINY, RemoteTypes } from "./setting.const.js";
|
|
1
|
+
import type { ChunkAlgorithms, E2EEAlgorithms, HashAlgorithms, MODE_AUTOMATIC, MODE_PAUSED, MODE_SELECTIVE, MODE_SHINY, P2PConnectionPath, RemoteTypes } from "./setting.const.js";
|
|
2
2
|
import type { CustomRegExpSourceList } from "./shared.type.util.js";
|
|
3
3
|
import type { JWTAlgorithm } from "./auth.type.js";
|
|
4
4
|
/** Display languages recognised by the current LiveSync settings schema. */
|
|
@@ -528,6 +528,16 @@ export interface P2PConnectionInfo {
|
|
|
528
528
|
* The TURN credential (password, secret, etc...) for the P2P connection.
|
|
529
529
|
*/
|
|
530
530
|
P2P_turnCredential: string;
|
|
531
|
+
/**
|
|
532
|
+
* Maximum serialised RPC wire payload sent through Trystero before
|
|
533
|
+
* Commonlib splits it. Applies to outgoing messages.
|
|
534
|
+
*/
|
|
535
|
+
P2P_maxWirePayloadBytes?: number;
|
|
536
|
+
/**
|
|
537
|
+
* ICE route policy for this P2P profile. Relay-only mode is effective only
|
|
538
|
+
* when the profile contains at least one valid TURN URL.
|
|
539
|
+
*/
|
|
540
|
+
P2P_connectionPath?: P2PConnectionPath;
|
|
531
541
|
/**
|
|
532
542
|
* Use Diagnostic Wrapper for RTCPeerConnection to collect statistics.
|
|
533
543
|
*/
|
|
@@ -152,6 +152,14 @@ const configurationNames = {
|
|
|
152
152
|
desc: "The credential/password for the TURN servers.",
|
|
153
153
|
isHidden: true
|
|
154
154
|
},
|
|
155
|
+
P2P_maxWirePayloadBytes: {
|
|
156
|
+
name: "P2P Message Size",
|
|
157
|
+
desc: "The maximum outgoing RPC wire payload before Commonlib splits it for the P2P transport."
|
|
158
|
+
},
|
|
159
|
+
P2P_connectionPath: {
|
|
160
|
+
name: "Connection Path",
|
|
161
|
+
desc: "Select the WebRTC route automatically or require a configured TURN relay."
|
|
162
|
+
},
|
|
155
163
|
useOnlyLocalChunk: {
|
|
156
164
|
name: "Use Only Local Chunks",
|
|
157
165
|
desc: "If enabled, the plugin will not attempt to connect to the remote database even if the chunk was not found locally.",
|
|
@@ -186,6 +186,8 @@ export declare const TweakValuesTemplate: {
|
|
|
186
186
|
P2P_turnServers?: string;
|
|
187
187
|
P2P_turnUsername?: string;
|
|
188
188
|
P2P_turnCredential?: string;
|
|
189
|
+
P2P_maxWirePayloadBytes?: number;
|
|
190
|
+
P2P_connectionPath?: import("./setting.const.js").P2PConnectionPath;
|
|
189
191
|
P2P_useDiagRTC?: boolean;
|
|
190
192
|
};
|
|
191
193
|
export type TweakValues = Partial<typeof TweakValuesTemplate>;
|
|
@@ -196,6 +196,8 @@ export declare const AllSettingDefault: {
|
|
|
196
196
|
P2P_turnServers: string;
|
|
197
197
|
P2P_turnUsername: string;
|
|
198
198
|
P2P_turnCredential: string;
|
|
199
|
+
P2P_maxWirePayloadBytes?: number;
|
|
200
|
+
P2P_connectionPath?: import("./types.js").P2PConnectionPath;
|
|
199
201
|
P2P_useDiagRTC?: boolean;
|
|
200
202
|
};
|
|
201
203
|
export type AllSettings = ObsidianLiveSyncSettings & OnDialogSettings;
|
package/dist/common/types.d.ts
CHANGED
|
@@ -8,7 +8,8 @@ import { ChunkTypes, EntryTypes, MILESTONE_DOCID, NODEINFO_DOCID, NoteTypes, SYN
|
|
|
8
8
|
import { AUTO_MERGED, CANCELLED, LEAVE_TO_SUBSEQUENT, MISSING_OR_ERROR, NOT_CONFLICTED, TIME_ARGUMENT_INFINITY } from "./models/shared.const.symbols.js";
|
|
9
9
|
import { IDPrefixes, LEAF_WAIT_ONLY_REMOTE, LEAF_WAIT_TIMEOUT, LEAF_WAIT_TIMEOUT_SEQUENTIAL_REPLICATOR, MAX_DOC_SIZE, MAX_DOC_SIZE_BIN, PREFIX_CHUNK, PREFIX_ENCRYPTED_CHUNK, PREFIX_OBFUSCATED, RECENT_MODIFIED_DOCS_QTY, REPLICATION_BUSY_TIMEOUT, SALT_OF_ID, SALT_OF_PASSPHRASE, SEED_MURMURHASH, VER } from "./models/shared.const.behabiour.js";
|
|
10
10
|
import { AutoAccepting, type BucketSyncSetting, type ChunkSplitterVersion, type ConfigPassphraseStore, type CouchDBConnection, type E2EEAlgorithm, type EncryptionSettings, type HashAlgorithm, type HasSettings, type LocalDBSettings, type ObsidianLiveSyncSettings, type P2PConnectionInfo, type P2PSyncSetting, type PluginSyncSettingEntry, type RemoteDBSettings, type RemoteType, type RemoteTypeSettings, type SYNC_MODE } from "./models/setting.type.js";
|
|
11
|
-
import { ChunkAlgorithmNames, ChunkAlgorithms, CURRENT_SETTING_VERSION, E2EEAlgorithmNames, E2EEAlgorithms, HashAlgorithms, REMOTE_COUCHDB, REMOTE_MINIO, REMOTE_P2P, RemoteTypes, SETTING_VERSION_INITIAL, SETTING_VERSION_SUPPORT_CASE_INSENSITIVE, MODE_AUTOMATIC, MODE_PAUSED, MODE_SELECTIVE, MODE_SHINY } from "./models/setting.const.js";
|
|
11
|
+
import { ChunkAlgorithmNames, ChunkAlgorithms, CURRENT_SETTING_VERSION, E2EEAlgorithmNames, E2EEAlgorithms, HashAlgorithms, REMOTE_COUCHDB, REMOTE_MINIO, REMOTE_P2P, RemoteTypes, SETTING_VERSION_INITIAL, SETTING_VERSION_SUPPORT_CASE_INSENSITIVE, MODE_AUTOMATIC, MODE_PAUSED, MODE_SELECTIVE, MODE_SHINY, P2PConnectionPaths, P2PMessageSizePresets, type P2PConnectionPath } from "./models/setting.const.js";
|
|
12
|
+
import { hasValidP2PTurnServerUrl, isValidP2PTurnServerUrl, normaliseP2PConnectionPath, normaliseP2PMaxWirePayloadBytes, splitP2PTurnServerUrls } from "./models/setting.p2p.js";
|
|
12
13
|
import { PREFERRED_BASE, PREFERRED_JOURNAL_SYNC, PREFERRED_SETTING_CLOUDANT, PREFERRED_SETTING_SELF_HOSTED } from "./models/setting.const.preferred.js";
|
|
13
14
|
import { P2P_DEFAULT_SETTINGS, DEFAULT_SETTINGS, NEW_VAULT_SETTINGS, SETTINGS_SCHEMA_DEFAULTS, createNewVaultSettings } from "./models/setting.const.defaults.js";
|
|
14
15
|
import { prepareSettingsForLoad, SettingsMigrationReviewCodes, type PreparedSettings, type SettingsMigrationReviewCode, type SettingsMigrationReviewReason, type SettingsMigrationState } from "./models/setting.lifecycle.js";
|
|
@@ -34,8 +35,9 @@ export { MODE_SELECTIVE, MODE_AUTOMATIC, MODE_PAUSED, MODE_SHINY, type SYNC_MODE
|
|
|
34
35
|
export { type PluginSyncSettingEntry };
|
|
35
36
|
export { SETTING_VERSION_INITIAL, SETTING_VERSION_SUPPORT_CASE_INSENSITIVE, CURRENT_SETTING_VERSION };
|
|
36
37
|
export type { BucketSyncSetting, LocalDBSettings };
|
|
37
|
-
export { RemoteTypes, REMOTE_COUCHDB, REMOTE_MINIO, REMOTE_P2P, type RemoteType, AutoAccepting };
|
|
38
|
-
export type { P2PConnectionInfo, P2PSyncSetting };
|
|
38
|
+
export { RemoteTypes, REMOTE_COUCHDB, REMOTE_MINIO, REMOTE_P2P, P2PConnectionPaths, P2PMessageSizePresets, type RemoteType, AutoAccepting, };
|
|
39
|
+
export type { P2PConnectionInfo, P2PConnectionPath, P2PSyncSetting };
|
|
40
|
+
export { hasValidP2PTurnServerUrl, isValidP2PTurnServerUrl, normaliseP2PConnectionPath, normaliseP2PMaxWirePayloadBytes, splitP2PTurnServerUrls, };
|
|
39
41
|
export { P2P_DEFAULT_SETTINGS };
|
|
40
42
|
export type { RemoteTypeSettings };
|
|
41
43
|
export { E2EEAlgorithmNames, E2EEAlgorithms, type E2EEAlgorithm };
|
package/dist/common/types.js
CHANGED
|
@@ -59,8 +59,17 @@ import {
|
|
|
59
59
|
MODE_AUTOMATIC,
|
|
60
60
|
MODE_PAUSED,
|
|
61
61
|
MODE_SELECTIVE,
|
|
62
|
-
MODE_SHINY
|
|
62
|
+
MODE_SHINY,
|
|
63
|
+
P2PConnectionPaths,
|
|
64
|
+
P2PMessageSizePresets
|
|
63
65
|
} from "./models/setting.const.js";
|
|
66
|
+
import {
|
|
67
|
+
hasValidP2PTurnServerUrl,
|
|
68
|
+
isValidP2PTurnServerUrl,
|
|
69
|
+
normaliseP2PConnectionPath,
|
|
70
|
+
normaliseP2PMaxWirePayloadBytes,
|
|
71
|
+
splitP2PTurnServerUrls
|
|
72
|
+
} from "./models/setting.p2p.js";
|
|
64
73
|
import {
|
|
65
74
|
PREFERRED_BASE,
|
|
66
75
|
PREFERRED_JOURNAL_SYNC,
|
|
@@ -181,6 +190,8 @@ export {
|
|
|
181
190
|
NODEINFO_DOCID,
|
|
182
191
|
NOT_CONFLICTED,
|
|
183
192
|
NoteTypes,
|
|
193
|
+
P2PConnectionPaths,
|
|
194
|
+
P2PMessageSizePresets,
|
|
184
195
|
P2P_DEFAULT_SETTINGS,
|
|
185
196
|
PREFERRED_BASE,
|
|
186
197
|
PREFERRED_JOURNAL_SYNC,
|
|
@@ -225,7 +236,12 @@ export {
|
|
|
225
236
|
configURIBaseQR,
|
|
226
237
|
configurationNames,
|
|
227
238
|
createNewVaultSettings,
|
|
239
|
+
hasValidP2PTurnServerUrl,
|
|
228
240
|
isMetaEntry,
|
|
241
|
+
isValidP2PTurnServerUrl,
|
|
242
|
+
normaliseP2PConnectionPath,
|
|
243
|
+
normaliseP2PMaxWirePayloadBytes,
|
|
229
244
|
prepareSettingsForLoad,
|
|
245
|
+
splitP2PTurnServerUrls,
|
|
230
246
|
statusDisplay
|
|
231
247
|
};
|
package/dist/common/utils.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
PREFIX_OBFUSCATED,
|
|
8
8
|
SYNCINFO_ID
|
|
9
9
|
} from "./types.js";
|
|
10
|
+
import { normaliseP2PConnectionPath, normaliseP2PMaxWirePayloadBytes } from "./models/setting.p2p.js";
|
|
10
11
|
import { isErrorOfMissingDoc } from "../pouchdb/utils_couchdb.js";
|
|
11
12
|
import { replaceAll, replaceAllPairs } from "octagonal-wheels/string";
|
|
12
13
|
import { concatUInt8Array } from "octagonal-wheels/binary";
|
|
@@ -459,7 +460,9 @@ function pickP2PSyncSettings(setting) {
|
|
|
459
460
|
P2P_DevicePeerName: setting.P2P_DevicePeerName || "",
|
|
460
461
|
P2P_turnServers: setting.P2P_turnServers,
|
|
461
462
|
P2P_turnUsername: setting.P2P_turnUsername,
|
|
462
|
-
P2P_turnCredential: setting.P2P_turnCredential
|
|
463
|
+
P2P_turnCredential: setting.P2P_turnCredential,
|
|
464
|
+
P2P_maxWirePayloadBytes: normaliseP2PMaxWirePayloadBytes(setting.P2P_maxWirePayloadBytes),
|
|
465
|
+
P2P_connectionPath: normaliseP2PConnectionPath(setting.P2P_connectionPath)
|
|
463
466
|
};
|
|
464
467
|
}
|
|
465
468
|
function wrapByDefault(func, onError) {
|
|
@@ -4,6 +4,16 @@ export interface DatabaseFileAccess {
|
|
|
4
4
|
store: (file: UXFileInfo, force?: boolean, skipCheck?: boolean) => Promise<boolean>;
|
|
5
5
|
/** Store a file as a child of an exact revision and return the created revision. */
|
|
6
6
|
storeWithBaseRevision: (file: UXFileInfo, baseRevision: string | undefined, skipCheck?: boolean) => Promise<string | false>;
|
|
7
|
+
/**
|
|
8
|
+
* Store a file as a child of an exact revision only while that revision remains a live leaf.
|
|
9
|
+
*
|
|
10
|
+
* Unlike {@link storeWithBaseRevision}, this method does not force a branch below an obsolete
|
|
11
|
+
* revision. 'Live leaf' means any current revision-tree leaf, including a non-winning conflict
|
|
12
|
+
* leaf or a logical-deletion leaf. In particular, it returns `false` when another writer has
|
|
13
|
+
* already advanced the supplied base; ordinary target and Chunk validation can also refuse the
|
|
14
|
+
* write without creating a Metadata successor.
|
|
15
|
+
*/
|
|
16
|
+
storeWithLiveBaseRevision: (file: UXFileInfo, baseRevision: string, skipCheck?: boolean) => Promise<string | false>;
|
|
7
17
|
storeAsConflictedRevision: (file: UXFileInfo, currentRev: string, skipCheck?: boolean) => Promise<boolean>;
|
|
8
18
|
/** Preserve unknown storage content as a conflict and return its exact revision. */
|
|
9
19
|
storeAsConflictedRevisionWithResult: (file: UXFileInfo, currentRev: string, skipCheck?: boolean) => Promise<string | false>;
|
|
@@ -35,4 +35,5 @@ export declare class EntryManager {
|
|
|
35
35
|
deleteDBEntry(path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions): Promise<boolean>;
|
|
36
36
|
storeDeletionAtRevision(path: FilePathWithPrefix | FilePath, baseRevision: string): Promise<false | PouchDB.Core.Response>;
|
|
37
37
|
putDBEntry(note: SavingEntry, onlyChunks?: boolean, conflictBaseRev?: string): Promise<false | PouchDB.Core.Response>;
|
|
38
|
+
putDBEntryWithLiveBaseRevision(note: SavingEntry, baseRevision: string, onlyChunks?: boolean): Promise<false | PouchDB.Core.Response>;
|
|
38
39
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
isTargetFile,
|
|
10
10
|
prepareChunk,
|
|
11
11
|
putDBEntry,
|
|
12
|
+
putDBEntryWithLiveBaseRevision,
|
|
12
13
|
storeDeletionByPathAtRevision
|
|
13
14
|
} from "./EntryManagerImpls.js";
|
|
14
15
|
class EntryManager {
|
|
@@ -71,6 +72,9 @@ class EntryManager {
|
|
|
71
72
|
async putDBEntry(note, onlyChunks, conflictBaseRev) {
|
|
72
73
|
return await putDBEntry(this.serviceHost, this, note, onlyChunks, conflictBaseRev);
|
|
73
74
|
}
|
|
75
|
+
async putDBEntryWithLiveBaseRevision(note, baseRevision, onlyChunks) {
|
|
76
|
+
return await putDBEntryWithLiveBaseRevision(this.serviceHost, this, note, baseRevision, onlyChunks);
|
|
77
|
+
}
|
|
74
78
|
}
|
|
75
79
|
export {
|
|
76
80
|
EntryManager
|
|
@@ -13,6 +13,13 @@ type Managers = {
|
|
|
13
13
|
type NecessaryManagers<T extends keyof Managers> = Pick<Managers, T>;
|
|
14
14
|
export declare function createChunks(managers: NecessaryManagers<"chunkManager" | "hashManager" | "splitter">, dispFilename: string, note: SavingEntry): Promise<false | DocumentID[]>;
|
|
15
15
|
export declare function putDBEntry(host: NecessaryServicesInterfaces<"path" | "setting", never>, managers: NecessaryManagers<"localDatabase" | "chunkManager" | "hashManager" | "splitter">, note: SavingEntry, onlyChunks?: boolean, conflictBaseRev?: string): Promise<false | PouchDB.Core.Response>;
|
|
16
|
+
/**
|
|
17
|
+
* Store an entry below an exact revision only while that revision remains a live leaf.
|
|
18
|
+
*
|
|
19
|
+
* PouchDB's ordinary MVCC write is the authority for this check. A 409 response means that the
|
|
20
|
+
* base has already been advanced, so the caller receives `false` and can retry from fresh state.
|
|
21
|
+
*/
|
|
22
|
+
export declare function putDBEntryWithLiveBaseRevision(host: NecessaryServicesInterfaces<"path" | "setting", never>, managers: NecessaryManagers<"localDatabase" | "chunkManager" | "hashManager" | "splitter">, note: SavingEntry, baseRevision: string, onlyChunks?: boolean): Promise<false | PouchDB.Core.Response>;
|
|
16
23
|
export declare function isTargetFile(host: NecessaryServicesInterfaces<"setting", never>, filenameSrc: string): boolean;
|
|
17
24
|
export declare function prepareChunk({ chunkManager, hashManager }: NecessaryManagers<"chunkManager" | "hashManager">, piece: string): Promise<GeneratedChunk>;
|
|
18
25
|
export declare function getDBEntryMetaByPath(host: NecessaryServicesInterfaces<"path" | "setting", never>, { localDatabase }: NecessaryManagers<"localDatabase">, path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions, includeDeleted?: boolean): Promise<false | LoadedEntry>;
|
|
@@ -110,6 +110,23 @@ async function createChunks(managers, dispFilename, note) {
|
|
|
110
110
|
return chunks;
|
|
111
111
|
}
|
|
112
112
|
async function putDBEntry(host, managers, note, onlyChunks, conflictBaseRev) {
|
|
113
|
+
const revisionTarget = conflictBaseRev ? { mode: "force-base", baseRevision: conflictBaseRev } : { mode: "latest" };
|
|
114
|
+
return await putDBEntryInternal(host, managers, note, onlyChunks, revisionTarget);
|
|
115
|
+
}
|
|
116
|
+
async function putDBEntryWithLiveBaseRevision(host, managers, note, baseRevision, onlyChunks) {
|
|
117
|
+
try {
|
|
118
|
+
return await putDBEntryInternal(host, managers, note, onlyChunks, {
|
|
119
|
+
mode: "live-base",
|
|
120
|
+
baseRevision
|
|
121
|
+
});
|
|
122
|
+
} catch (ex) {
|
|
123
|
+
if (ex && typeof ex === "object" && "status" in ex && ex.status === 409) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
throw ex;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function putDBEntryInternal(host, managers, note, onlyChunks, revisionTarget) {
|
|
113
130
|
const { localDatabase, chunkManager, splitter } = managers;
|
|
114
131
|
const filename = host.services.path.id2path(note._id, note);
|
|
115
132
|
const dispFilename = stripAllPrefixes(filename);
|
|
@@ -146,8 +163,8 @@ async function putDBEntry(host, managers, note, onlyChunks, conflictBaseRev) {
|
|
|
146
163
|
eden: {}
|
|
147
164
|
};
|
|
148
165
|
return await serialized("file:" + filename, async () => {
|
|
149
|
-
if (
|
|
150
|
-
newDoc._rev =
|
|
166
|
+
if (revisionTarget.mode !== "latest") {
|
|
167
|
+
newDoc._rev = revisionTarget.baseRevision;
|
|
151
168
|
} else {
|
|
152
169
|
try {
|
|
153
170
|
const old = await localDatabase.get(newDoc._id);
|
|
@@ -159,7 +176,7 @@ async function putDBEntry(host, managers, note, onlyChunks, conflictBaseRev) {
|
|
|
159
176
|
}
|
|
160
177
|
}
|
|
161
178
|
}
|
|
162
|
-
const r = await localDatabase.put(newDoc, { force: true });
|
|
179
|
+
const r = revisionTarget.mode === "live-base" ? await localDatabase.put(newDoc) : await localDatabase.put(newDoc, { force: true });
|
|
163
180
|
if (r.ok) {
|
|
164
181
|
return r;
|
|
165
182
|
} else {
|
|
@@ -523,5 +540,6 @@ export {
|
|
|
523
540
|
isTargetFile,
|
|
524
541
|
prepareChunk,
|
|
525
542
|
putDBEntry,
|
|
543
|
+
putDBEntryWithLiveBaseRevision,
|
|
526
544
|
storeDeletionByPathAtRevision
|
|
527
545
|
};
|
|
@@ -192,6 +192,7 @@ export declare class LiveSyncLocalDB {
|
|
|
192
192
|
deleteDBEntry(path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions): Promise<boolean>;
|
|
193
193
|
storeDeletionAtRevision(path: FilePathWithPrefix | FilePath, baseRevision: string): Promise<false | PouchDB.Core.Response>;
|
|
194
194
|
putDBEntry(note: SavingEntry, onlyChunks?: boolean, conflictBaseRev?: string): Promise<false | PouchDB.Core.Response>;
|
|
195
|
+
putDBEntryWithLiveBaseRevision(note: SavingEntry, baseRevision: string, onlyChunks?: boolean): Promise<false | PouchDB.Core.Response>;
|
|
195
196
|
getConflictedDoc(path: FilePathWithPrefix, rev: string): Promise<false | diff_result_leaf>;
|
|
196
197
|
tryAutoMerge(path: FilePathWithPrefix, enableMarkdownAutoMerge: boolean): AutoMergeResult;
|
|
197
198
|
}
|
|
@@ -370,6 +370,9 @@ class LiveSyncLocalDB {
|
|
|
370
370
|
async putDBEntry(note, onlyChunks, conflictBaseRev) {
|
|
371
371
|
return await this.managers.entryManager.putDBEntry(note, onlyChunks, conflictBaseRev);
|
|
372
372
|
}
|
|
373
|
+
async putDBEntryWithLiveBaseRevision(note, baseRevision, onlyChunks) {
|
|
374
|
+
return await this.managers.entryManager.putDBEntryWithLiveBaseRevision(note, baseRevision, onlyChunks);
|
|
375
|
+
}
|
|
373
376
|
async getConflictedDoc(path, rev) {
|
|
374
377
|
return await this.managers.conflictManager.getConflictedDoc(path, rev);
|
|
375
378
|
}
|
|
@@ -20,6 +20,7 @@ export declare class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicat
|
|
|
20
20
|
private _replicator?;
|
|
21
21
|
private _lifecycleOperation;
|
|
22
22
|
private _shouldBeOpen;
|
|
23
|
+
private _activeTransportCompatibilitySignature?;
|
|
23
24
|
get openReplicationUI(): (showResult: boolean) => Promise<boolean | void>;
|
|
24
25
|
get rawReplicator(): TrysteroReplicator;
|
|
25
26
|
get rawHost(): P2PHost;
|
|
@@ -27,6 +28,13 @@ export declare class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicat
|
|
|
27
28
|
terminateSync(): void;
|
|
28
29
|
private _buildEnv;
|
|
29
30
|
private _enqueueLifecycleOperation;
|
|
31
|
+
/**
|
|
32
|
+
* Capture settings which are fixed when the active P2P transport joins its room.
|
|
33
|
+
* A later open request can remain idempotent only while these effective values match.
|
|
34
|
+
*/
|
|
35
|
+
private _getTransportCompatibilitySignature;
|
|
36
|
+
/** Close and forget the transport without changing the requested lifecycle state. */
|
|
37
|
+
private _closeTransport;
|
|
30
38
|
open(): Promise<void>;
|
|
31
39
|
close(): Promise<void>;
|
|
32
40
|
closeReplication(): void;
|
|
@@ -16,11 +16,18 @@ import {
|
|
|
16
16
|
P2PHost
|
|
17
17
|
} from "./TrysteroReplicatorP2PServer.js";
|
|
18
18
|
import { delay } from "octagonal-wheels/promises";
|
|
19
|
+
import {
|
|
20
|
+
hasValidP2PTurnServerUrl,
|
|
21
|
+
normaliseP2PConnectionPath,
|
|
22
|
+
normaliseP2PMaxWirePayloadBytes
|
|
23
|
+
} from "../../common/models/setting.p2p.js";
|
|
24
|
+
import { P2PConnectionPaths } from "../../common/models/setting.const.js";
|
|
19
25
|
class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
|
|
20
26
|
_p2pHost;
|
|
21
27
|
_replicator;
|
|
22
28
|
_lifecycleOperation = Promise.resolve();
|
|
23
29
|
_shouldBeOpen = false;
|
|
30
|
+
_activeTransportCompatibilitySignature;
|
|
24
31
|
get openReplicationUI() {
|
|
25
32
|
return this.env.openReplicationUI;
|
|
26
33
|
}
|
|
@@ -81,6 +88,26 @@ class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
|
|
|
81
88
|
this._lifecycleOperation = queued.catch(() => void 0);
|
|
82
89
|
return queued;
|
|
83
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Capture settings which are fixed when the active P2P transport joins its room.
|
|
93
|
+
* A later open request can remain idempotent only while these effective values match.
|
|
94
|
+
*/
|
|
95
|
+
_getTransportCompatibilitySignature() {
|
|
96
|
+
const settings = this.env.services.setting.currentSettings();
|
|
97
|
+
const configuredPath = normaliseP2PConnectionPath(settings.P2P_connectionPath);
|
|
98
|
+
const effectivePath = configuredPath === P2PConnectionPaths.Relay && hasValidP2PTurnServerUrl(settings.P2P_turnServers ?? "") ? P2PConnectionPaths.Relay : P2PConnectionPaths.Automatic;
|
|
99
|
+
return `${normaliseP2PMaxWirePayloadBytes(settings.P2P_maxWirePayloadBytes)}:${effectivePath}`;
|
|
100
|
+
}
|
|
101
|
+
/** Close and forget the transport without changing the requested lifecycle state. */
|
|
102
|
+
async _closeTransport() {
|
|
103
|
+
if (this._replicator) {
|
|
104
|
+
this._replicator.disableBroadcastChanges();
|
|
105
|
+
await this._replicator.close();
|
|
106
|
+
this._replicator = void 0;
|
|
107
|
+
}
|
|
108
|
+
this._p2pHost = void 0;
|
|
109
|
+
this._activeTransportCompatibilitySignature = void 0;
|
|
110
|
+
}
|
|
84
111
|
async open() {
|
|
85
112
|
if (!this.env.services.setting.currentSettings().P2P_Enabled) {
|
|
86
113
|
Logger(this.translate("P2P.NotEnabled"), LOG_LEVEL_NOTICE);
|
|
@@ -89,9 +116,13 @@ class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
|
|
|
89
116
|
this._shouldBeOpen = true;
|
|
90
117
|
await this._enqueueLifecycleOperation(async () => {
|
|
91
118
|
if (!this._shouldBeOpen) return;
|
|
119
|
+
const compatibilitySignature = this._getTransportCompatibilitySignature();
|
|
92
120
|
if (this._replicator && this._p2pHost?.isServing) {
|
|
93
|
-
|
|
94
|
-
|
|
121
|
+
if (this._activeTransportCompatibilitySignature === compatibilitySignature) {
|
|
122
|
+
Logger("P2P replicator is already open.");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
await this._closeTransport();
|
|
95
126
|
}
|
|
96
127
|
try {
|
|
97
128
|
const env = this._buildEnv();
|
|
@@ -100,23 +131,20 @@ class LiveSyncTrysteroReplicator extends LiveSyncAbstractReplicator {
|
|
|
100
131
|
this._p2pHost = host;
|
|
101
132
|
this._replicator = replicator;
|
|
102
133
|
await replicator.open();
|
|
134
|
+
this._activeTransportCompatibilitySignature = compatibilitySignature;
|
|
103
135
|
} catch (e) {
|
|
104
136
|
Logger(e instanceof Error ? e.message : "Error while opening P2P connection", LOG_LEVEL_NOTICE);
|
|
105
137
|
Logger(e, LOG_LEVEL_VERBOSE);
|
|
106
138
|
this._p2pHost = void 0;
|
|
107
139
|
this._replicator = void 0;
|
|
140
|
+
this._activeTransportCompatibilitySignature = void 0;
|
|
108
141
|
}
|
|
109
142
|
});
|
|
110
143
|
}
|
|
111
144
|
async close() {
|
|
112
145
|
this._shouldBeOpen = false;
|
|
113
146
|
await this._enqueueLifecycleOperation(async () => {
|
|
114
|
-
|
|
115
|
-
this._replicator.disableBroadcastChanges();
|
|
116
|
-
await this._replicator.close();
|
|
117
|
-
this._replicator = void 0;
|
|
118
|
-
}
|
|
119
|
-
this._p2pHost = void 0;
|
|
147
|
+
await this._closeTransport();
|
|
120
148
|
});
|
|
121
149
|
}
|
|
122
150
|
closeReplication() {
|
|
@@ -13,7 +13,7 @@ import { EVENT_PLATFORM_UNLOADED } from "../../events/coreEvents.js";
|
|
|
13
13
|
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
|
-
import {
|
|
16
|
+
import { resolveTrysteroRpcOptions } from "../../rpc/transports/TrysteroTransport.js";
|
|
17
17
|
import { subscribeTrysteroPeerEvents } from "../../rpc/transports/trysteroRoomEvents.js";
|
|
18
18
|
import { toRpcMethodName } from "./rpcCompat.js";
|
|
19
19
|
import { generateJoinRoomOptions } from "../../rpc/transports/trysteroUtils.js";
|
|
@@ -379,7 +379,7 @@ You can chose as follows:
|
|
|
379
379
|
};
|
|
380
380
|
this._rpcRoom?.close();
|
|
381
381
|
this._rpcRoom = new RpcRoom({
|
|
382
|
-
...
|
|
382
|
+
...resolveTrysteroRpcOptions(this.settings),
|
|
383
383
|
transport,
|
|
384
384
|
canAcceptRequest: async (peerId, method) => {
|
|
385
385
|
if (method === toRpcMethodName("!reqAuth")) return true;
|
|
@@ -68,6 +68,8 @@ export declare const TRYSTERO_RPC_DEFAULTS: {
|
|
|
68
68
|
/** SCTP guarantees delivery; retransmission is a last-resort safety net. */
|
|
69
69
|
readonly chunkMissingRetryMs: 150;
|
|
70
70
|
};
|
|
71
|
+
/** Resolve the RpcRoom defaults contributed by one P2P connection profile. */
|
|
72
|
+
export declare function resolveTrysteroRpcOptions(settings: Pick<P2PConnectionInfo, "P2P_maxWirePayloadBytes">): Required<Pick<RpcRoomOptions, "maxWirePayloadBytes" | "chunkMissingRetryMs">>;
|
|
71
73
|
/**
|
|
72
74
|
* Wrap an already-joined Trystero `Room` as a `TransportAdapter` for `RpcRoom`.
|
|
73
75
|
*
|
|
@@ -2,6 +2,7 @@ import { joinRoom, selfId } from "@trystero-p2p/nostr";
|
|
|
2
2
|
import { RpcRoom } from "../RpcRoom.js";
|
|
3
3
|
import { exposeDB } from "../pouchdb/RpcPouchDBServer.js";
|
|
4
4
|
import { RpcPouchDBProxy } from "../pouchdb/RpcPouchDBProxy.js";
|
|
5
|
+
import { normaliseP2PMaxWirePayloadBytes } from "../../common/models/setting.p2p.js";
|
|
5
6
|
import { ConnectionStringParser } from "../../common/ConnectionString.js";
|
|
6
7
|
import { compatGlobal } from "../../common/coreEnvFunctions.js";
|
|
7
8
|
import { generateJoinRoomOptions } from "./trysteroUtils.js";
|
|
@@ -46,6 +47,12 @@ const TRYSTERO_RPC_DEFAULTS = {
|
|
|
46
47
|
/** SCTP guarantees delivery; retransmission is a last-resort safety net. */
|
|
47
48
|
chunkMissingRetryMs: 150
|
|
48
49
|
};
|
|
50
|
+
function resolveTrysteroRpcOptions(settings) {
|
|
51
|
+
return {
|
|
52
|
+
...TRYSTERO_RPC_DEFAULTS,
|
|
53
|
+
maxWirePayloadBytes: normaliseP2PMaxWirePayloadBytes(settings.P2P_maxWirePayloadBytes)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
49
56
|
function wrapTrysteroRoom(room) {
|
|
50
57
|
const rpcAction = room.makeAction(RPC_ACTION_NAME);
|
|
51
58
|
return {
|
|
@@ -93,7 +100,11 @@ function joinTrysteroRoomFromUrl(url) {
|
|
|
93
100
|
}
|
|
94
101
|
function serveTrysteroDB(settings, db, ns = "pdb", options) {
|
|
95
102
|
const roomHandle = joinTrysteroRoom(settings);
|
|
96
|
-
const rpcRoom = new RpcRoom({
|
|
103
|
+
const rpcRoom = new RpcRoom({
|
|
104
|
+
...resolveTrysteroRpcOptions(settings),
|
|
105
|
+
transport: roomHandle.transport,
|
|
106
|
+
...options
|
|
107
|
+
});
|
|
97
108
|
exposeDB(rpcRoom, db, ns);
|
|
98
109
|
return {
|
|
99
110
|
peerId: roomHandle.peerId,
|
|
@@ -107,7 +118,11 @@ function serveTrysteroDB(settings, db, ns = "pdb", options) {
|
|
|
107
118
|
}
|
|
108
119
|
function connectTrysteroDBClient(settings, serverPeerId, dbName, ns = "pdb", options) {
|
|
109
120
|
const roomHandle = joinTrysteroRoom(settings);
|
|
110
|
-
const rpcRoom = new RpcRoom({
|
|
121
|
+
const rpcRoom = new RpcRoom({
|
|
122
|
+
...resolveTrysteroRpcOptions(settings),
|
|
123
|
+
transport: roomHandle.transport,
|
|
124
|
+
...options
|
|
125
|
+
});
|
|
111
126
|
const proxy = new RpcPouchDBProxy(rpcRoom.session(serverPeerId), dbName, ns);
|
|
112
127
|
return {
|
|
113
128
|
proxy,
|
|
@@ -136,6 +151,7 @@ export {
|
|
|
136
151
|
connectTrysteroDBClient,
|
|
137
152
|
joinTrysteroRoom,
|
|
138
153
|
joinTrysteroRoomFromUrl,
|
|
154
|
+
resolveTrysteroRpcOptions,
|
|
139
155
|
serveTrysteroDB,
|
|
140
156
|
wrapTrysteroRoom
|
|
141
157
|
};
|