@vrtmrz/livesync-commonlib 0.1.23 → 0.1.25
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/API/processSetting.d.ts +1 -1
- package/dist/API/processSetting.js +10 -6
- package/dist/common/ConnectionString.js +15 -8
- package/dist/common/models/setting.const.qr.js +5 -0
- package/dist/common/models/setting.lifecycle.js +5 -3
- package/dist/common/models/setting.p2p.d.ts +10 -0
- package/dist/common/models/setting.p2p.js +13 -0
- package/dist/common/models/setting.type.d.ts +10 -0
- package/dist/common/models/tweak.compatibility.d.ts +34 -0
- package/dist/common/models/tweak.compatibility.js +110 -0
- package/dist/common/models/tweak.definition.d.ts +110 -19
- package/dist/common/models/tweak.definition.js +62 -29
- package/dist/common/settingConstants.d.ts +5 -0
- package/dist/common/types.d.ts +6 -3
- package/dist/common/types.js +6 -0
- package/dist/common/utils.js +4 -1
- package/dist/p2p/P2PService.d.ts +7 -1
- package/dist/p2p/P2PService.js +13 -7
- package/dist/p2p/index.d.ts +3 -1
- package/dist/p2p/index.js +6 -0
- package/dist/pouchdb/LiveSyncDBFunctions.d.ts +3 -3
- package/dist/pouchdb/LiveSyncDBFunctions.js +9 -15
- package/dist/replication/CentralCompatibility.d.ts +4 -2
- package/dist/replication/CentralCompatibility.js +5 -3
- package/dist/replication/couchdb/LiveSyncReplicator.js +10 -2
- package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +2 -2
- package/dist/replication/journal/LiveSyncJournalReplicator.js +18 -4
- package/dist/replication/trystero/P2PRoomSessionOwner.d.ts +22 -1
- package/dist/replication/trystero/P2PRoomSessionOwner.js +193 -13
- package/dist/replication/trystero/TrysteroReplicator.js +12 -7
- package/dist/replication/trystero/TrysteroReplicatorP2PServer.d.ts +1 -0
- package/dist/replication/trystero/TrysteroReplicatorP2PServer.js +10 -3
- package/dist/replication/trystero/p2pReplicatorConfigurationIdentity.js +6 -3
- package/dist/replication/trystero/useP2PReplicatorFeature.d.ts +6 -1
- package/dist/replication/trystero/useP2PReplicatorFeature.js +7 -4
- package/dist/rpc/transports/trysteroUtils.d.ts +2 -2
- package/dist/rpc/transports/trysteroUtils.js +32 -2
- package/dist/serviceFeatures/remoteConfig.js +3 -0
- package/dist/services/base/IService.d.ts +3 -3
- package/dist/services/base/ReplicationService.d.ts +2 -2
- package/dist/services/base/ReplicationService.js +14 -11
- package/dist/services/base/SettingService.d.ts +1 -0
- package/dist/services/base/SettingService.js +37 -9
- package/dist/services/base/TweakValueService.d.ts +5 -3
- package/dist/services/implements/injectable/InjectableTweakValueService.d.ts +5 -5
- package/dist/settings.d.ts +3 -0
- package/dist/settings.js +2 -0
- package/dist/worker/bgWorker.js +1 -1
- package/docs/development.md +3 -3
- package/docs/p2p-transport-lifecycle.md +86 -4
- package/docs/proven-in-use.md +32 -0
- package/docs/settings-lifecycle.md +20 -1
- package/package.json +1 -1
|
@@ -37,5 +37,5 @@ type ErasureProperties = keyof ObsidianLiveSyncSettings;
|
|
|
37
37
|
* @returns Generated setup URI
|
|
38
38
|
*/
|
|
39
39
|
export declare function encodeSettingsToSetupURI(settingString: ObsidianLiveSyncSettings, passphrase: string, removeProperties?: ErasureProperties[], skipDefaultValue?: boolean): Promise<string>;
|
|
40
|
-
export declare function decodeSettingsFromSetupURI(uri: string, passphrase: string): Promise<false | ObsidianLiveSyncSettings
|
|
40
|
+
export declare function decodeSettingsFromSetupURI(uri: string, passphrase: string): Promise<false | Omit<ObsidianLiveSyncSettings, "P2P_iceServers" | "P2P_iceServersExpiresAt">>;
|
|
41
41
|
export {};
|
|
@@ -4,11 +4,13 @@ import { decodeAnyArray, encodeAnyArray } from "octagonal-wheels/object";
|
|
|
4
4
|
import {
|
|
5
5
|
DEFAULT_SETTINGS,
|
|
6
6
|
KeyIndexOfSettings,
|
|
7
|
-
LOG_LEVEL_NOTICE
|
|
7
|
+
LOG_LEVEL_NOTICE,
|
|
8
|
+
omitP2PRuntimeSettings
|
|
8
9
|
} from "../common/types.js";
|
|
9
10
|
import { decryptString, encryptString } from "../encryption/stringEncryption.js";
|
|
10
11
|
import { LOG_LEVEL_VERBOSE, Logger } from "octagonal-wheels/common/logger";
|
|
11
12
|
function encodeSettingsToQRCodeData(settings) {
|
|
13
|
+
settings = omitP2PRuntimeSettings(settings);
|
|
12
14
|
const fullIndexes = Object.entries(KeyIndexOfSettings);
|
|
13
15
|
let maxIndex = 0;
|
|
14
16
|
for (const [, index] of fullIndexes) {
|
|
@@ -48,7 +50,7 @@ function decodeSettingsFromQRCodeData(qr) {
|
|
|
48
50
|
LOG_LEVEL_VERBOSE
|
|
49
51
|
);
|
|
50
52
|
}
|
|
51
|
-
return newSettings;
|
|
53
|
+
return omitP2PRuntimeSettings(newSettings);
|
|
52
54
|
}
|
|
53
55
|
var OutputFormat = /* @__PURE__ */ ((OutputFormat2) => {
|
|
54
56
|
OutputFormat2[OutputFormat2["SVG"] = 0] = "SVG";
|
|
@@ -99,8 +101,11 @@ const necessaryErasureProperties = [
|
|
|
99
101
|
];
|
|
100
102
|
async function encodeSettingsToSetupURI(settingString, passphrase, removeProperties = ["pluginSyncExtendedSetting"], skipDefaultValue = false) {
|
|
101
103
|
const setting = {
|
|
102
|
-
...settingString
|
|
104
|
+
...omitP2PRuntimeSettings(settingString)
|
|
103
105
|
};
|
|
106
|
+
delete setting.P2P_managedType;
|
|
107
|
+
delete setting.P2P_managedId;
|
|
108
|
+
delete setting.P2P_managedToken;
|
|
104
109
|
if (skipDefaultValue) {
|
|
105
110
|
const keys = Object.keys(setting);
|
|
106
111
|
for (const k of keys) {
|
|
@@ -123,10 +128,9 @@ async function decodeSettingsFromSetupURI(uri, passphrase) {
|
|
|
123
128
|
const encryptedSetting = uri.substring(configURIBase.length);
|
|
124
129
|
const decrypted = await decryptString(decodeURIComponent(encryptedSetting), passphrase);
|
|
125
130
|
try {
|
|
126
|
-
return JSON.parse(decrypted);
|
|
127
|
-
} catch
|
|
131
|
+
return omitP2PRuntimeSettings(JSON.parse(decrypted));
|
|
132
|
+
} catch {
|
|
128
133
|
Logger(`Failed to parse settings from decrypted data`, LOG_LEVEL_NOTICE);
|
|
129
|
-
Logger(e, LOG_LEVEL_VERBOSE);
|
|
130
134
|
return false;
|
|
131
135
|
}
|
|
132
136
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
normaliseP2PConnectionPath,
|
|
3
|
+
normaliseP2PMaxWirePayloadBytes
|
|
4
|
+
} from "./models/setting.p2p.js";
|
|
2
5
|
const PROXY_SCHEME = "https";
|
|
3
6
|
function parseSlsUri(uriString) {
|
|
4
7
|
const match = uriString.match(/^sls\+([^:]+):(.*)$/);
|
|
5
8
|
if (!match) {
|
|
6
|
-
throw new Error(
|
|
9
|
+
throw new Error("Unsupported URI");
|
|
7
10
|
}
|
|
8
11
|
const subscheme = match[1];
|
|
9
12
|
const rest = match[2];
|
|
@@ -20,7 +23,7 @@ class ConnectionStringParser {
|
|
|
20
23
|
static parse(uriString) {
|
|
21
24
|
const match = uriString.match(/^sls\+([^:]+):/);
|
|
22
25
|
if (!match) {
|
|
23
|
-
throw new Error(
|
|
26
|
+
throw new Error("Unsupported URI");
|
|
24
27
|
}
|
|
25
28
|
const subscheme = match[1];
|
|
26
29
|
if (subscheme === "p2p") {
|
|
@@ -43,7 +46,7 @@ class ConnectionStringParser {
|
|
|
43
46
|
settings: this.parseS3(url)
|
|
44
47
|
};
|
|
45
48
|
default:
|
|
46
|
-
throw new Error(
|
|
49
|
+
throw new Error("Unsupported protocol");
|
|
47
50
|
}
|
|
48
51
|
}
|
|
49
52
|
/**
|
|
@@ -126,9 +129,7 @@ class ConnectionStringParser {
|
|
|
126
129
|
}
|
|
127
130
|
static parseP2P(uriString) {
|
|
128
131
|
const match = uriString.match(/^sls\+p2p:\/\/([^?#]+)(?:\?([^#]*))?(?:#(.*))?$/);
|
|
129
|
-
if (!match)
|
|
130
|
-
throw new Error(`Invalid P2P URI: ${uriString}`);
|
|
131
|
-
}
|
|
132
|
+
if (!match) throw new Error("Invalid P2P URI");
|
|
132
133
|
const authority = match[1];
|
|
133
134
|
const queryString = match[2] || "";
|
|
134
135
|
let userinfo = "";
|
|
@@ -158,7 +159,10 @@ class ConnectionStringParser {
|
|
|
158
159
|
P2P_turnUsername: searchParams.get("turnUser") || "",
|
|
159
160
|
P2P_turnCredential: searchParams.get("turnPass") || "",
|
|
160
161
|
P2P_maxWirePayloadBytes: normaliseP2PMaxWirePayloadBytes(Number(searchParams.get("maxWirePayloadBytes"))),
|
|
161
|
-
P2P_connectionPath: normaliseP2PConnectionPath(searchParams.get("connectionPath"))
|
|
162
|
+
P2P_connectionPath: normaliseP2PConnectionPath(searchParams.get("connectionPath")),
|
|
163
|
+
P2P_managedType: searchParams.get("managedType") || void 0,
|
|
164
|
+
P2P_managedId: searchParams.get("managedId") || void 0,
|
|
165
|
+
P2P_managedToken: searchParams.get("token") || void 0
|
|
162
166
|
};
|
|
163
167
|
}
|
|
164
168
|
static serializeP2P(settings) {
|
|
@@ -176,6 +180,9 @@ class ConnectionStringParser {
|
|
|
176
180
|
String(normaliseP2PMaxWirePayloadBytes(settings.P2P_maxWirePayloadBytes))
|
|
177
181
|
);
|
|
178
182
|
searchParams.set("connectionPath", normaliseP2PConnectionPath(settings.P2P_connectionPath));
|
|
183
|
+
if (settings.P2P_managedType) searchParams.set("managedType", settings.P2P_managedType);
|
|
184
|
+
if (settings.P2P_managedId) searchParams.set("managedId", settings.P2P_managedId);
|
|
185
|
+
if (settings.P2P_managedToken) searchParams.set("token", settings.P2P_managedToken);
|
|
179
186
|
const credentials = settings.P2P_passphrase ? `:${encodeURIComponent(settings.P2P_passphrase)}@` : "";
|
|
180
187
|
const host = encodeURIComponent(settings.P2P_roomID);
|
|
181
188
|
const query = searchParams.toString() ? `?${searchParams.toString()}` : "";
|
|
@@ -159,6 +159,11 @@ const KeyIndexOfSettings = {
|
|
|
159
159
|
P2P_turnServers: 150,
|
|
160
160
|
P2P_turnUsername: 151,
|
|
161
161
|
P2P_turnCredential: 152,
|
|
162
|
+
P2P_managedType: -1,
|
|
163
|
+
P2P_managedId: -1,
|
|
164
|
+
P2P_managedToken: -1,
|
|
165
|
+
P2P_iceServers: -1,
|
|
166
|
+
P2P_iceServersExpiresAt: -1,
|
|
162
167
|
syncInternalFileOverwritePatterns: 153,
|
|
163
168
|
useOnlyLocalChunk: 154,
|
|
164
169
|
maxMTimeForReflectEvents: 155,
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
SETTING_VERSION_INITIAL,
|
|
5
5
|
SETTING_VERSION_SUPPORT_CASE_INSENSITIVE
|
|
6
6
|
} from "./setting.const.js";
|
|
7
|
+
import { omitP2PRuntimeSettings } from "./setting.p2p.js";
|
|
7
8
|
const SettingsMigrationReviewCodes = {
|
|
8
9
|
LegacyUpdatePending: "legacy-update-review-pending",
|
|
9
10
|
FutureSchema: "settings-schema-newer-than-runtime",
|
|
@@ -22,18 +23,19 @@ const SETTINGS_MIGRATIONS = [
|
|
|
22
23
|
}
|
|
23
24
|
];
|
|
24
25
|
function cloneSettings(source, fallbacks) {
|
|
26
|
+
const persistableSource = omitP2PRuntimeSettings(source);
|
|
25
27
|
return {
|
|
26
28
|
...fallbacks,
|
|
27
|
-
...
|
|
29
|
+
...persistableSource,
|
|
28
30
|
remoteConfigurations: Object.fromEntries(
|
|
29
|
-
Object.entries(
|
|
31
|
+
Object.entries(persistableSource.remoteConfigurations ?? fallbacks.remoteConfigurations).map(([id, config]) => [
|
|
30
32
|
id,
|
|
31
33
|
{ ...config }
|
|
32
34
|
])
|
|
33
35
|
),
|
|
34
36
|
pluginSyncExtendedSetting: {
|
|
35
37
|
...fallbacks.pluginSyncExtendedSetting,
|
|
36
|
-
...
|
|
38
|
+
...persistableSource.pluginSyncExtendedSetting
|
|
37
39
|
}
|
|
38
40
|
};
|
|
39
41
|
}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { type P2PConnectionPath } from "./setting.const.js";
|
|
2
|
+
import type { P2PConnectionInfo, P2PSyncSetting } from "./setting.type.js";
|
|
3
|
+
/** Report whether the selected P2P settings request host-managed TURN credentials. */
|
|
4
|
+
export declare function hasManagedP2PTurnConfiguration(settings: Partial<Pick<P2PConnectionInfo, "P2P_managedType">>): boolean;
|
|
5
|
+
/** Remove connection-only ICE credentials before settings cross a serialisation boundary. */
|
|
6
|
+
export declare function omitP2PRuntimeSettings<T extends Partial<P2PSyncSetting>>(settings: T): Omit<T, "P2P_iceServers" | "P2P_iceServersExpiresAt">;
|
|
7
|
+
/**
|
|
8
|
+
* Report whether a P2P profile has a usable manual TURN endpoint or requests
|
|
9
|
+
* host-managed TURN credentials. STUN-only profiles remain false.
|
|
10
|
+
*/
|
|
11
|
+
export declare function hasP2PTurnConfiguration(settings: Partial<Pick<P2PConnectionInfo, "P2P_turnServers" | "P2P_managedType">>): boolean;
|
|
2
12
|
/**
|
|
3
13
|
* Return a safe outgoing RPC wire-payload bound for Trystero.
|
|
4
14
|
*
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { P2PConnectionPaths, P2PMessageSizePresets } from "./setting.const.js";
|
|
2
|
+
function hasManagedP2PTurnConfiguration(settings) {
|
|
3
|
+
return typeof settings.P2P_managedType === "string" && settings.P2P_managedType.trim().length > 0;
|
|
4
|
+
}
|
|
5
|
+
function omitP2PRuntimeSettings(settings) {
|
|
6
|
+
const { P2P_iceServers: _iceServers, P2P_iceServersExpiresAt: _expiresAt, ...persistable } = settings;
|
|
7
|
+
return persistable;
|
|
8
|
+
}
|
|
9
|
+
function hasP2PTurnConfiguration(settings) {
|
|
10
|
+
return hasManagedP2PTurnConfiguration(settings) || typeof settings.P2P_turnServers === "string" && hasValidP2PTurnServerUrl(settings.P2P_turnServers);
|
|
11
|
+
}
|
|
2
12
|
function normaliseP2PMaxWirePayloadBytes(value) {
|
|
3
13
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < P2PMessageSizePresets.MaximumCompatibility || value > P2PMessageSizePresets.Standard) {
|
|
4
14
|
return P2PMessageSizePresets.Standard;
|
|
@@ -31,10 +41,13 @@ function hasValidP2PTurnServerUrl(value) {
|
|
|
31
41
|
return splitP2PTurnServerUrls(value).some(isValidP2PTurnServerUrl);
|
|
32
42
|
}
|
|
33
43
|
export {
|
|
44
|
+
hasManagedP2PTurnConfiguration,
|
|
45
|
+
hasP2PTurnConfiguration,
|
|
34
46
|
hasValidP2PTurnServerUrl,
|
|
35
47
|
isValidP2PTurnServerUrl,
|
|
36
48
|
normaliseP2PConnectionPath,
|
|
37
49
|
normaliseP2PMaxWirePayloadBytes,
|
|
50
|
+
omitP2PRuntimeSettings,
|
|
38
51
|
splitP2PRelayUrls,
|
|
39
52
|
splitP2PTurnServerUrls
|
|
40
53
|
};
|
|
@@ -528,6 +528,12 @@ export interface P2PConnectionInfo {
|
|
|
528
528
|
* The TURN credential (password, secret, etc...) for the P2P connection.
|
|
529
529
|
*/
|
|
530
530
|
P2P_turnCredential: string;
|
|
531
|
+
/** Host-defined managed TURN provider type. */
|
|
532
|
+
P2P_managedType?: string;
|
|
533
|
+
/** Host-defined managed TURN provider identifier. */
|
|
534
|
+
P2P_managedId?: string;
|
|
535
|
+
/** Host-defined managed TURN provider token. */
|
|
536
|
+
P2P_managedToken?: string;
|
|
531
537
|
/**
|
|
532
538
|
* Maximum serialised RPC wire payload sent through Trystero before
|
|
533
539
|
* Commonlib splits it. Applies to outgoing messages.
|
|
@@ -552,6 +558,10 @@ export interface P2PSyncSetting extends P2PConnectionInfo {
|
|
|
552
558
|
P2P_AutoAcceptingPeers: string;
|
|
553
559
|
P2P_AutoDenyingPeers: string;
|
|
554
560
|
P2P_IsHeadless?: boolean;
|
|
561
|
+
/** ICE servers prepared for one room connection. Never persist or share this value. */
|
|
562
|
+
P2P_iceServers?: readonly RTCIceServer[];
|
|
563
|
+
/** Absolute Unix timestamp in milliseconds for the prepared ICE servers. */
|
|
564
|
+
P2P_iceServersExpiresAt?: number;
|
|
555
565
|
}
|
|
556
566
|
/**
|
|
557
567
|
* Interface representing the settings for a remote type.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type TweakReconstruction, type TweakValues } from "./tweak.definition.js";
|
|
2
|
+
export type TweakAssessmentRelation = "equal" | "different" | "unadvertised";
|
|
3
|
+
export interface TweakAssessmentValue {
|
|
4
|
+
readonly present: boolean;
|
|
5
|
+
readonly rawValue: TweakValues[keyof TweakValues];
|
|
6
|
+
readonly effectiveValue: TweakValues[keyof TweakValues];
|
|
7
|
+
}
|
|
8
|
+
export interface TweakAssessmentEntry {
|
|
9
|
+
readonly key: keyof TweakValues;
|
|
10
|
+
readonly current: TweakAssessmentValue;
|
|
11
|
+
readonly preferred: TweakAssessmentValue;
|
|
12
|
+
readonly relation: TweakAssessmentRelation;
|
|
13
|
+
}
|
|
14
|
+
export interface TweakTransitionReason {
|
|
15
|
+
readonly key: keyof TweakValues;
|
|
16
|
+
readonly reconstruction: Exclude<TweakReconstruction, "none">;
|
|
17
|
+
}
|
|
18
|
+
export interface TweakTransition {
|
|
19
|
+
readonly changes: Readonly<TweakValues>;
|
|
20
|
+
readonly reconstruction: TweakReconstruction;
|
|
21
|
+
readonly reasons: readonly TweakTransitionReason[];
|
|
22
|
+
}
|
|
23
|
+
export interface TweakAssessment {
|
|
24
|
+
readonly alignment: "matched" | "mismatched";
|
|
25
|
+
readonly representationDiffers: boolean;
|
|
26
|
+
readonly entries: readonly TweakAssessmentEntry[];
|
|
27
|
+
readonly adoptPreferred: TweakTransition;
|
|
28
|
+
readonly adoptCurrent: TweakTransition;
|
|
29
|
+
readonly onlyCompatibleLossyDifferences: boolean;
|
|
30
|
+
readonly currentValues: Readonly<TweakValues>;
|
|
31
|
+
readonly preferredValues: Readonly<TweakValues>;
|
|
32
|
+
}
|
|
33
|
+
/** Assess the semantic and stored-representation relationship between two tweak snapshots. */
|
|
34
|
+
export declare function assessTweakCompatibility(current: Readonly<TweakValues>, preferred: Readonly<TweakValues>): TweakAssessment;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TweakCompatibilityRules,
|
|
3
|
+
TweakValuesTemplate
|
|
4
|
+
} from "./tweak.definition.js";
|
|
5
|
+
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
6
|
+
function snapshotTweakValues(values) {
|
|
7
|
+
const snapshot = {};
|
|
8
|
+
for (const key of Object.keys(TweakValuesTemplate)) {
|
|
9
|
+
if (hasOwn(values, key)) snapshot[key] = values[key];
|
|
10
|
+
}
|
|
11
|
+
return Object.freeze(snapshot);
|
|
12
|
+
}
|
|
13
|
+
function assessedValue(values, key, knownDefault) {
|
|
14
|
+
const present = hasOwn(values, key);
|
|
15
|
+
const rawValue = values[key];
|
|
16
|
+
const effectiveValue = rawValue === void 0 ? knownDefault : rawValue;
|
|
17
|
+
return Object.freeze({ present, rawValue, effectiveValue });
|
|
18
|
+
}
|
|
19
|
+
function highestReconstruction(current, next) {
|
|
20
|
+
if (current === "required" || next === "required") return "required";
|
|
21
|
+
return "recommended";
|
|
22
|
+
}
|
|
23
|
+
function reconstructionForTransition(key, from, to) {
|
|
24
|
+
const rule = TweakCompatibilityRules.find((candidate) => candidate.key === key);
|
|
25
|
+
if (!rule) return "none";
|
|
26
|
+
let reconstruction = rule.reconstruction;
|
|
27
|
+
const extensibleRule = rule;
|
|
28
|
+
for (const pattern of extensibleRule.specificPatterns ?? []) {
|
|
29
|
+
const matchesFrom = hasOwn(pattern, "from") && pattern.from === from;
|
|
30
|
+
const matchesTo = hasOwn(pattern, "to") && pattern.to === to;
|
|
31
|
+
if (matchesFrom || matchesTo) {
|
|
32
|
+
reconstruction = highestReconstruction(reconstruction, pattern.reconstruction);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return reconstruction;
|
|
36
|
+
}
|
|
37
|
+
function transition(entries, sourceSide, targetSide) {
|
|
38
|
+
const changes = {};
|
|
39
|
+
const reasons = [];
|
|
40
|
+
let reconstruction = "none";
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
const source = entry[sourceSide];
|
|
43
|
+
const target = entry[targetSide];
|
|
44
|
+
const sourceAdvertised = source.present && source.rawValue !== void 0;
|
|
45
|
+
const targetAdvertised = target.present && target.rawValue !== void 0;
|
|
46
|
+
const effectiveValueDiffers = source.effectiveValue !== target.effectiveValue;
|
|
47
|
+
const shouldFillMissingTarget = !targetAdvertised && sourceAdvertised;
|
|
48
|
+
if (source.effectiveValue === void 0 || !effectiveValueDiffers && !shouldFillMissingTarget) continue;
|
|
49
|
+
changes[entry.key] = source.effectiveValue;
|
|
50
|
+
if (entry.relation === "equal") continue;
|
|
51
|
+
const effect = reconstructionForTransition(entry.key, target.effectiveValue, source.effectiveValue);
|
|
52
|
+
if (effect === "none") continue;
|
|
53
|
+
const reason = Object.freeze({
|
|
54
|
+
key: entry.key,
|
|
55
|
+
reconstruction: effect
|
|
56
|
+
});
|
|
57
|
+
reasons.push(reason);
|
|
58
|
+
reconstruction = highestReconstruction(reconstruction, reason.reconstruction);
|
|
59
|
+
}
|
|
60
|
+
return Object.freeze({
|
|
61
|
+
changes: Object.freeze(changes),
|
|
62
|
+
reconstruction,
|
|
63
|
+
reasons: Object.freeze(reasons)
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function assessTweakCompatibility(current, preferred) {
|
|
67
|
+
const currentValues = snapshotTweakValues(current);
|
|
68
|
+
const preferredValues = snapshotTweakValues(preferred);
|
|
69
|
+
let representationDiffers = false;
|
|
70
|
+
const entries = TweakCompatibilityRules.map((rule) => {
|
|
71
|
+
const knownDefault = "knownDefault" in rule ? rule.knownDefault : void 0;
|
|
72
|
+
const currentValue = assessedValue(currentValues, rule.key, knownDefault);
|
|
73
|
+
const preferredValue = assessedValue(preferredValues, rule.key, knownDefault);
|
|
74
|
+
const currentAdvertised = currentValue.present && currentValue.rawValue !== void 0;
|
|
75
|
+
const preferredAdvertised = preferredValue.present && preferredValue.rawValue !== void 0;
|
|
76
|
+
let relation;
|
|
77
|
+
if ((!currentAdvertised || !preferredAdvertised) && knownDefault === void 0) {
|
|
78
|
+
relation = "unadvertised";
|
|
79
|
+
} else {
|
|
80
|
+
relation = currentValue.effectiveValue === preferredValue.effectiveValue ? "equal" : "different";
|
|
81
|
+
}
|
|
82
|
+
if (currentValue.present !== preferredValue.present || currentValue.rawValue !== preferredValue.rawValue) {
|
|
83
|
+
representationDiffers = true;
|
|
84
|
+
}
|
|
85
|
+
return Object.freeze({
|
|
86
|
+
key: rule.key,
|
|
87
|
+
current: currentValue,
|
|
88
|
+
preferred: preferredValue,
|
|
89
|
+
relation
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
const frozenEntries = Object.freeze(entries);
|
|
93
|
+
const differences = frozenEntries.filter(({ relation }) => relation === "different");
|
|
94
|
+
const compatibleLossyKeys = new Set(
|
|
95
|
+
TweakCompatibilityRules.filter((rule) => "compatibleLossyOrder" in rule).map(({ key }) => key)
|
|
96
|
+
);
|
|
97
|
+
return Object.freeze({
|
|
98
|
+
alignment: differences.length === 0 ? "matched" : "mismatched",
|
|
99
|
+
representationDiffers,
|
|
100
|
+
entries: frozenEntries,
|
|
101
|
+
adoptPreferred: transition(frozenEntries, "preferred", "current"),
|
|
102
|
+
adoptCurrent: transition(frozenEntries, "current", "preferred"),
|
|
103
|
+
onlyCompatibleLossyDifferences: differences.length > 0 && differences.every(({ key }) => compatibleLossyKeys.has(key)) && !frozenEntries.some(({ relation }) => relation === "unadvertised"),
|
|
104
|
+
currentValues,
|
|
105
|
+
preferredValues
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
export {
|
|
109
|
+
assessTweakCompatibility
|
|
110
|
+
};
|
|
@@ -1,5 +1,91 @@
|
|
|
1
1
|
import type { ObsidianLiveSyncSettings } from "./setting.type.js";
|
|
2
|
-
export
|
|
2
|
+
export type TweakReconstruction = "none" | "recommended" | "required";
|
|
3
|
+
/**
|
|
4
|
+
* Single source for tweak comparison semantics and the maintained legacy
|
|
5
|
+
* compatibility projections below.
|
|
6
|
+
*/
|
|
7
|
+
export declare const TweakCompatibilityRules: readonly [{
|
|
8
|
+
readonly key: "minimumChunkSize";
|
|
9
|
+
readonly templateValue: 20;
|
|
10
|
+
readonly reconstruction: "none";
|
|
11
|
+
}, {
|
|
12
|
+
readonly key: "longLineThreshold";
|
|
13
|
+
readonly templateValue: 250;
|
|
14
|
+
readonly reconstruction: "none";
|
|
15
|
+
}, {
|
|
16
|
+
readonly key: "encrypt";
|
|
17
|
+
readonly templateValue: false;
|
|
18
|
+
readonly reconstruction: "required";
|
|
19
|
+
}, {
|
|
20
|
+
readonly key: "usePathObfuscation";
|
|
21
|
+
readonly templateValue: false;
|
|
22
|
+
readonly reconstruction: "required";
|
|
23
|
+
}, {
|
|
24
|
+
readonly key: "enableCompression";
|
|
25
|
+
readonly templateValue: false;
|
|
26
|
+
readonly reconstruction: "none";
|
|
27
|
+
}, {
|
|
28
|
+
readonly key: "useEden";
|
|
29
|
+
readonly templateValue: false;
|
|
30
|
+
readonly reconstruction: "none";
|
|
31
|
+
}, {
|
|
32
|
+
readonly key: "customChunkSize";
|
|
33
|
+
readonly templateValue: 0;
|
|
34
|
+
readonly reconstruction: "recommended";
|
|
35
|
+
readonly compatibleLossyOrder: 1;
|
|
36
|
+
}, {
|
|
37
|
+
readonly key: "useDynamicIterationCount";
|
|
38
|
+
readonly templateValue: false;
|
|
39
|
+
readonly reconstruction: "required";
|
|
40
|
+
}, {
|
|
41
|
+
readonly key: "hashAlg";
|
|
42
|
+
readonly templateValue: "xxhash64";
|
|
43
|
+
readonly reconstruction: "recommended";
|
|
44
|
+
readonly compatibleLossyOrder: 0;
|
|
45
|
+
}, {
|
|
46
|
+
readonly key: "enableChunkSplitterV2";
|
|
47
|
+
readonly templateValue: false;
|
|
48
|
+
readonly reconstruction: "none";
|
|
49
|
+
}, {
|
|
50
|
+
readonly key: "maxChunksInEden";
|
|
51
|
+
readonly templateValue: 10;
|
|
52
|
+
readonly reconstruction: "none";
|
|
53
|
+
}, {
|
|
54
|
+
readonly key: "maxTotalLengthInEden";
|
|
55
|
+
readonly templateValue: 1024;
|
|
56
|
+
readonly reconstruction: "none";
|
|
57
|
+
}, {
|
|
58
|
+
readonly key: "maxAgeInEden";
|
|
59
|
+
readonly templateValue: 10;
|
|
60
|
+
readonly reconstruction: "none";
|
|
61
|
+
}, {
|
|
62
|
+
readonly key: "usePluginSyncV2";
|
|
63
|
+
readonly templateValue: false;
|
|
64
|
+
readonly reconstruction: "none";
|
|
65
|
+
readonly knownDefault: false;
|
|
66
|
+
}, {
|
|
67
|
+
readonly key: "handleFilenameCaseSensitive";
|
|
68
|
+
readonly templateValue: false;
|
|
69
|
+
readonly reconstruction: "required";
|
|
70
|
+
readonly knownDefault: false;
|
|
71
|
+
}, {
|
|
72
|
+
readonly key: "useSegmenter";
|
|
73
|
+
readonly templateValue: false;
|
|
74
|
+
readonly reconstruction: "none";
|
|
75
|
+
}, {
|
|
76
|
+
readonly key: "E2EEAlgorithm";
|
|
77
|
+
readonly templateValue: "v2";
|
|
78
|
+
readonly reconstruction: "none";
|
|
79
|
+
readonly knownDefault: import("./setting.type.js").E2EEAlgorithm;
|
|
80
|
+
}, {
|
|
81
|
+
readonly key: "chunkSplitterVersion";
|
|
82
|
+
readonly templateValue: "v3-rabin-karp";
|
|
83
|
+
readonly reconstruction: "recommended";
|
|
84
|
+
readonly knownDefault: import("./setting.type.js").ChunkSplitterVersion;
|
|
85
|
+
readonly compatibleLossyOrder: 2;
|
|
86
|
+
}];
|
|
87
|
+
export type TweakComparedKey = (typeof TweakCompatibilityRules)[number]["key"];
|
|
88
|
+
export declare const TweakValuesShouldMatchedTemplate: Pick<ObsidianLiveSyncSettings, TweakComparedKey>;
|
|
3
89
|
type TweakKeys = keyof TweakValues;
|
|
4
90
|
export declare const IncompatibleChanges: TweakKeys[];
|
|
5
91
|
export declare const CompatibleButLossyChanges: TweakKeys[];
|
|
@@ -19,6 +105,24 @@ export declare const TweakValuesRecommendedTemplate: Partial<ObsidianLiveSyncSet
|
|
|
19
105
|
export declare const TweakValuesDefault: Partial<ObsidianLiveSyncSettings>;
|
|
20
106
|
export declare const TweakValuesTemplate: {
|
|
21
107
|
tweakModified: number;
|
|
108
|
+
usePluginSyncV2: boolean;
|
|
109
|
+
encrypt: boolean;
|
|
110
|
+
usePathObfuscation: boolean;
|
|
111
|
+
E2EEAlgorithm: import("./setting.type.js").E2EEAlgorithm;
|
|
112
|
+
hashAlg: import("./setting.type.js").HashAlgorithm;
|
|
113
|
+
minimumChunkSize: number;
|
|
114
|
+
customChunkSize: number;
|
|
115
|
+
longLineThreshold: number;
|
|
116
|
+
useSegmenter: boolean;
|
|
117
|
+
enableChunkSplitterV2: boolean;
|
|
118
|
+
chunkSplitterVersion: import("./setting.type.js").ChunkSplitterVersion;
|
|
119
|
+
useEden: boolean;
|
|
120
|
+
maxChunksInEden: number;
|
|
121
|
+
maxTotalLengthInEden: number;
|
|
122
|
+
maxAgeInEden: number;
|
|
123
|
+
useDynamicIterationCount: boolean;
|
|
124
|
+
enableCompression: boolean;
|
|
125
|
+
handleFilenameCaseSensitive: boolean;
|
|
22
126
|
liveSync?: boolean;
|
|
23
127
|
syncOnSave?: boolean;
|
|
24
128
|
syncOnStart?: boolean;
|
|
@@ -72,7 +176,6 @@ export declare const TweakValuesTemplate: {
|
|
|
72
176
|
autoSweepPluginsPeriodic?: boolean;
|
|
73
177
|
notifyPluginOrSettingUpdated?: boolean;
|
|
74
178
|
deviceAndVaultName?: string;
|
|
75
|
-
usePluginSyncV2?: boolean;
|
|
76
179
|
usePluginEtc?: boolean;
|
|
77
180
|
pluginSyncExtendedSetting?: Record<import("./setting.type.js").PluginSyncSettingEntry["key"], import("./setting.type.js").PluginSyncSettingEntry>;
|
|
78
181
|
useAdvancedMode?: boolean;
|
|
@@ -124,34 +227,18 @@ export declare const TweakValuesTemplate: {
|
|
|
124
227
|
bucketPrefix?: string;
|
|
125
228
|
forcePathStyle?: boolean;
|
|
126
229
|
remoteType?: import("./setting.type.js").RemoteType;
|
|
127
|
-
encrypt?: boolean;
|
|
128
230
|
passphrase?: string;
|
|
129
|
-
usePathObfuscation?: boolean;
|
|
130
|
-
E2EEAlgorithm?: import("./setting.type.js").E2EEAlgorithm;
|
|
131
|
-
hashAlg?: import("./setting.type.js").HashAlgorithm;
|
|
132
|
-
minimumChunkSize?: number;
|
|
133
|
-
customChunkSize?: number;
|
|
134
|
-
longLineThreshold?: number;
|
|
135
|
-
useSegmenter?: boolean;
|
|
136
|
-
enableChunkSplitterV2?: boolean;
|
|
137
231
|
doNotUseFixedRevisionForChunks?: boolean;
|
|
138
|
-
chunkSplitterVersion?: import("./setting.type.js").ChunkSplitterVersion;
|
|
139
|
-
useEden?: boolean;
|
|
140
|
-
maxChunksInEden?: number;
|
|
141
|
-
maxTotalLengthInEden?: number;
|
|
142
|
-
maxAgeInEden?: number;
|
|
143
232
|
checkIntegrityOnSave?: boolean;
|
|
144
233
|
useHistory?: boolean;
|
|
145
234
|
disableRequestURI?: boolean;
|
|
146
235
|
sendChunksBulk?: boolean;
|
|
147
236
|
sendChunksBulkMaxSize?: number;
|
|
148
|
-
useDynamicIterationCount?: boolean;
|
|
149
237
|
doNotPaceReplication?: boolean;
|
|
150
238
|
readChunksOnline?: boolean;
|
|
151
239
|
useOnlyLocalChunk?: boolean;
|
|
152
240
|
concurrencyOfReadChunksOnline?: number;
|
|
153
241
|
minimumIntervalOfReadChunksOnline?: number;
|
|
154
|
-
enableCompression?: boolean;
|
|
155
242
|
batch_size?: number;
|
|
156
243
|
batches_limit?: number;
|
|
157
244
|
ignoreVersionCheck?: boolean;
|
|
@@ -160,7 +247,6 @@ export declare const TweakValuesTemplate: {
|
|
|
160
247
|
hashCacheMaxCount?: number;
|
|
161
248
|
hashCacheMaxAmount?: number;
|
|
162
249
|
permitEmptyPassphrase?: boolean;
|
|
163
|
-
handleFilenameCaseSensitive?: boolean;
|
|
164
250
|
checkConflictOnlyOnOpen?: boolean;
|
|
165
251
|
showMergeDialogOnlyOnActive?: boolean;
|
|
166
252
|
additionalSuffixOfDatabaseName?: string | undefined;
|
|
@@ -175,6 +261,8 @@ export declare const TweakValuesTemplate: {
|
|
|
175
261
|
P2P_AutoAcceptingPeers?: string;
|
|
176
262
|
P2P_AutoDenyingPeers?: string;
|
|
177
263
|
P2P_IsHeadless?: boolean;
|
|
264
|
+
P2P_iceServers?: readonly RTCIceServer[];
|
|
265
|
+
P2P_iceServersExpiresAt?: number;
|
|
178
266
|
P2P_Enabled?: boolean;
|
|
179
267
|
P2P_relays?: string;
|
|
180
268
|
P2P_roomID?: string;
|
|
@@ -186,6 +274,9 @@ export declare const TweakValuesTemplate: {
|
|
|
186
274
|
P2P_turnServers?: string;
|
|
187
275
|
P2P_turnUsername?: string;
|
|
188
276
|
P2P_turnCredential?: string;
|
|
277
|
+
P2P_managedType?: string;
|
|
278
|
+
P2P_managedId?: string;
|
|
279
|
+
P2P_managedToken?: string;
|
|
189
280
|
P2P_maxWirePayloadBytes?: number;
|
|
190
281
|
P2P_connectionPath?: import("./setting.const.js").P2PConnectionPath;
|
|
191
282
|
P2P_useDiagRTC?: boolean;
|