@vrtmrz/livesync-commonlib 0.1.24 → 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.
Files changed (34) hide show
  1. package/dist/API/processSetting.d.ts +1 -1
  2. package/dist/API/processSetting.js +10 -6
  3. package/dist/common/ConnectionString.js +15 -8
  4. package/dist/common/models/setting.const.qr.js +5 -0
  5. package/dist/common/models/setting.lifecycle.js +5 -3
  6. package/dist/common/models/setting.p2p.d.ts +10 -0
  7. package/dist/common/models/setting.p2p.js +13 -0
  8. package/dist/common/models/setting.type.d.ts +10 -0
  9. package/dist/common/models/tweak.definition.d.ts +5 -0
  10. package/dist/common/settingConstants.d.ts +5 -0
  11. package/dist/common/types.d.ts +3 -3
  12. package/dist/common/types.js +6 -0
  13. package/dist/common/utils.js +4 -1
  14. package/dist/p2p/P2PService.d.ts +7 -1
  15. package/dist/p2p/P2PService.js +13 -7
  16. package/dist/p2p/index.d.ts +3 -1
  17. package/dist/p2p/index.js +6 -0
  18. package/dist/replication/trystero/P2PRoomSessionOwner.d.ts +22 -1
  19. package/dist/replication/trystero/P2PRoomSessionOwner.js +193 -13
  20. package/dist/replication/trystero/TrysteroReplicator.js +9 -3
  21. package/dist/replication/trystero/TrysteroReplicatorP2PServer.d.ts +1 -0
  22. package/dist/replication/trystero/TrysteroReplicatorP2PServer.js +10 -3
  23. package/dist/replication/trystero/p2pReplicatorConfigurationIdentity.js +6 -3
  24. package/dist/replication/trystero/useP2PReplicatorFeature.d.ts +6 -1
  25. package/dist/replication/trystero/useP2PReplicatorFeature.js +7 -4
  26. package/dist/rpc/transports/trysteroUtils.d.ts +2 -2
  27. package/dist/rpc/transports/trysteroUtils.js +32 -2
  28. package/dist/serviceFeatures/remoteConfig.js +3 -0
  29. package/dist/services/base/SettingService.d.ts +1 -0
  30. package/dist/services/base/SettingService.js +37 -9
  31. package/docs/development.md +3 -3
  32. package/docs/p2p-transport-lifecycle.md +86 -4
  33. package/docs/proven-in-use.md +30 -0
  34. 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 (e) {
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 { normaliseP2PConnectionPath, normaliseP2PMaxWirePayloadBytes } from "./models/setting.p2p.js";
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(`Unsupported URI: ${uriString}`);
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(`Unsupported URI: ${uriString}`);
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(`Unsupported protocol: sls+${subscheme}`);
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
- ...source,
29
+ ...persistableSource,
28
30
  remoteConfigurations: Object.fromEntries(
29
- Object.entries(source.remoteConfigurations ?? fallbacks.remoteConfigurations).map(([id, config]) => [
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
- ...source.pluginSyncExtendedSetting
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.
@@ -261,6 +261,8 @@ export declare const TweakValuesTemplate: {
261
261
  P2P_AutoAcceptingPeers?: string;
262
262
  P2P_AutoDenyingPeers?: string;
263
263
  P2P_IsHeadless?: boolean;
264
+ P2P_iceServers?: readonly RTCIceServer[];
265
+ P2P_iceServersExpiresAt?: number;
264
266
  P2P_Enabled?: boolean;
265
267
  P2P_relays?: string;
266
268
  P2P_roomID?: string;
@@ -272,6 +274,9 @@ export declare const TweakValuesTemplate: {
272
274
  P2P_turnServers?: string;
273
275
  P2P_turnUsername?: string;
274
276
  P2P_turnCredential?: string;
277
+ P2P_managedType?: string;
278
+ P2P_managedId?: string;
279
+ P2P_managedToken?: string;
275
280
  P2P_maxWirePayloadBytes?: number;
276
281
  P2P_connectionPath?: import("./setting.const.js").P2PConnectionPath;
277
282
  P2P_useDiagRTC?: boolean;
@@ -185,6 +185,8 @@ export declare const AllSettingDefault: {
185
185
  P2P_AutoAcceptingPeers: string;
186
186
  P2P_AutoDenyingPeers: string;
187
187
  P2P_IsHeadless?: boolean;
188
+ P2P_iceServers?: readonly RTCIceServer[];
189
+ P2P_iceServersExpiresAt?: number;
188
190
  P2P_Enabled: boolean;
189
191
  P2P_relays: string;
190
192
  P2P_roomID: string;
@@ -196,6 +198,9 @@ export declare const AllSettingDefault: {
196
198
  P2P_turnServers: string;
197
199
  P2P_turnUsername: string;
198
200
  P2P_turnCredential: string;
201
+ P2P_managedType?: string;
202
+ P2P_managedId?: string;
203
+ P2P_managedToken?: string;
199
204
  P2P_maxWirePayloadBytes?: number;
200
205
  P2P_connectionPath?: import("./types.js").P2PConnectionPath;
201
206
  P2P_useDiagRTC?: boolean;
@@ -9,7 +9,7 @@ import { AUTO_MERGED, CANCELLED, LEAVE_TO_SUBSEQUENT, MISSING_OR_ERROR, NOT_CONF
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
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
+ import { hasManagedP2PTurnConfiguration, hasP2PTurnConfiguration, hasValidP2PTurnServerUrl, omitP2PRuntimeSettings, isValidP2PTurnServerUrl, normaliseP2PConnectionPath, normaliseP2PMaxWirePayloadBytes, splitP2PTurnServerUrls } from "./models/setting.p2p.js";
13
13
  import { PREFERRED_BASE, PREFERRED_JOURNAL_SYNC, PREFERRED_SETTING_CLOUDANT, PREFERRED_SETTING_SELF_HOSTED } from "./models/setting.const.preferred.js";
14
14
  import { P2P_DEFAULT_SETTINGS, DEFAULT_SETTINGS, NEW_VAULT_SETTINGS, SETTINGS_SCHEMA_DEFAULTS, createNewVaultSettings } from "./models/setting.const.defaults.js";
15
15
  import { prepareSettingsForLoad, SettingsMigrationReviewCodes, type PreparedSettings, type SettingsMigrationReviewCode, type SettingsMigrationReviewReason, type SettingsMigrationState } from "./models/setting.lifecycle.js";
@@ -39,7 +39,7 @@ export { SETTING_VERSION_INITIAL, SETTING_VERSION_SUPPORT_CASE_INSENSITIVE, CURR
39
39
  export type { BucketSyncSetting, LocalDBSettings };
40
40
  export { RemoteTypes, REMOTE_COUCHDB, REMOTE_MINIO, REMOTE_P2P, P2PConnectionPaths, P2PMessageSizePresets, type RemoteType, AutoAccepting, };
41
41
  export type { P2PConnectionInfo, P2PConnectionPath, P2PSyncSetting };
42
- export { hasValidP2PTurnServerUrl, isValidP2PTurnServerUrl, normaliseP2PConnectionPath, normaliseP2PMaxWirePayloadBytes, splitP2PTurnServerUrls, };
42
+ export { hasManagedP2PTurnConfiguration, hasP2PTurnConfiguration, hasValidP2PTurnServerUrl, omitP2PRuntimeSettings, isValidP2PTurnServerUrl, normaliseP2PConnectionPath, normaliseP2PMaxWirePayloadBytes, splitP2PTurnServerUrls, };
43
43
  export { P2P_DEFAULT_SETTINGS };
44
44
  export type { RemoteTypeSettings };
45
45
  export { E2EEAlgorithmNames, E2EEAlgorithms, type E2EEAlgorithm };
@@ -116,4 +116,4 @@ export { DOCID_SYNC_PARAMETERS };
116
116
  export { DOCID_JOURNAL_SYNC_PARAMETERS };
117
117
  export type { SyncParameters };
118
118
  export { DEFAULT_SYNC_PARAMETERS };
119
- export { SETTING_KEY_P2P_DEVICE_NAME, configURIBase, configURIBaseQR, SuffixDatabaseName, ExtraSuffixIndexedDB };
119
+ export { SETTING_KEY_P2P_DEVICE_NAME, configURIBase, configURIBaseQR, SuffixDatabaseName, ExtraSuffixIndexedDB, };
@@ -64,7 +64,10 @@ import {
64
64
  P2PMessageSizePresets
65
65
  } from "./models/setting.const.js";
66
66
  import {
67
+ hasManagedP2PTurnConfiguration,
68
+ hasP2PTurnConfiguration,
67
69
  hasValidP2PTurnServerUrl,
70
+ omitP2PRuntimeSettings,
68
71
  isValidP2PTurnServerUrl,
69
72
  normaliseP2PConnectionPath,
70
73
  normaliseP2PMaxWirePayloadBytes,
@@ -236,11 +239,14 @@ export {
236
239
  configURIBaseQR,
237
240
  configurationNames,
238
241
  createNewVaultSettings,
242
+ hasManagedP2PTurnConfiguration,
243
+ hasP2PTurnConfiguration,
239
244
  hasValidP2PTurnServerUrl,
240
245
  isMetaEntry,
241
246
  isValidP2PTurnServerUrl,
242
247
  normaliseP2PConnectionPath,
243
248
  normaliseP2PMaxWirePayloadBytes,
249
+ omitP2PRuntimeSettings,
244
250
  prepareSettingsForLoad,
245
251
  splitP2PTurnServerUrls,
246
252
  statusDisplay
@@ -462,7 +462,10 @@ function pickP2PSyncSettings(setting) {
462
462
  P2P_turnUsername: setting.P2P_turnUsername,
463
463
  P2P_turnCredential: setting.P2P_turnCredential,
464
464
  P2P_maxWirePayloadBytes: normaliseP2PMaxWirePayloadBytes(setting.P2P_maxWirePayloadBytes),
465
- P2P_connectionPath: normaliseP2PConnectionPath(setting.P2P_connectionPath)
465
+ P2P_connectionPath: normaliseP2PConnectionPath(setting.P2P_connectionPath),
466
+ P2P_managedType: setting.P2P_managedType,
467
+ P2P_managedId: setting.P2P_managedId,
468
+ P2P_managedToken: setting.P2P_managedToken
466
469
  };
467
470
  }
468
471
  function wrapByDefault(func, onError) {
@@ -5,6 +5,7 @@ import { LiveSyncTrysteroReplicator, type LiveSyncTrysteroReplicatorEnv } from "
5
5
  import type { P2PReplicationResult } from "../replication/trystero/TrysteroReplicator.js";
6
6
  import type { AcceptanceDecision, RevokeAcceptanceDecision } from "../replication/trystero/TrysteroReplicatorP2PServer.js";
7
7
  import type { Advertisement } from "../replication/trystero/types.js";
8
+ import { type PrepareP2PSettings } from "../replication/trystero/P2PRoomSessionOwner.js";
8
9
  import { type ReplicationOutcome } from "../replication/ReplicatorProvider.js";
9
10
  import type { P2PConnectionProbeAdmission } from "../replication/trystero/P2PConnectionProbeAdmission.js";
10
11
  export { ACTIVE_P2P_RELAY_BINDING_CONFLICT } from "../replication/trystero/P2PConnectionProbeAdmission.js";
@@ -114,10 +115,15 @@ export interface P2PServiceComposition {
114
115
  /** Create a non-owning adapter for the active Replicator selection. */
115
116
  createActiveReplicator(): ReplicatorInstance;
116
117
  }
118
+ /** Optional host dependencies used by the private P2P service composition. */
119
+ export interface P2PServiceOptions {
120
+ readonly prepareP2PSettings?: PrepareP2PSettings;
121
+ }
122
+ export type { PrepareP2PSettings };
117
123
  /**
118
124
  * Compose stable capability views over one private P2P service context.
119
125
  *
120
126
  * `P2PRoomSessionOwner` remains the resource owner. The returned composition
121
127
  * object is host wiring, not a capability façade for ordinary consumers.
122
128
  */
123
- export declare function createP2PService(env: LiveSyncTrysteroReplicatorEnv): P2PServiceComposition;
129
+ export declare function createP2PService(env: LiveSyncTrysteroReplicatorEnv, options?: P2PServiceOptions): P2PServiceComposition;
@@ -2,7 +2,9 @@ import { getPeerConnectionStats } from "../rpc/transports/DiagRTCPeerConnections
2
2
  import {
3
3
  LiveSyncTrysteroReplicator
4
4
  } from "../replication/trystero/LiveSyncTrysteroReplicator.js";
5
- import { P2PRoomSessionOwner } from "../replication/trystero/P2PRoomSessionOwner.js";
5
+ import {
6
+ P2PRoomSessionOwner
7
+ } from "../replication/trystero/P2PRoomSessionOwner.js";
6
8
  import { compatGlobal } from "../common/coreEnvFunctions.js";
7
9
  import {
8
10
  REPLICATION_CANCELLED,
@@ -75,12 +77,16 @@ function closeForLifecycle(owner, state) {
75
77
  cancelDelayedAutomation(owner, state);
76
78
  return owner.close();
77
79
  }
78
- function reconcileAutoStart(context, settings) {
80
+ async function reconcileAutoStart(context, settings) {
79
81
  if (!settings.P2P_Enabled || !settings.P2P_AutoStart) {
80
- return context.roomSessionOwner.setPersistentDemand("automatic", false);
82
+ await context.roomSessionOwner.setPersistentDemand("automatic", false);
83
+ return;
84
+ }
85
+ if (context.state.explicitDisconnectVeto || context.state.lifecycleClosed) return;
86
+ try {
87
+ await context.roomSessionOwner.setPersistentDemand("automatic", true);
88
+ } catch {
81
89
  }
82
- if (context.state.explicitDisconnectVeto || context.state.lifecycleClosed) return Promise.resolve();
83
- return context.roomSessionOwner.setPersistentDemand("automatic", true);
84
90
  }
85
91
  function scheduleAutoStart(context, delayMs = 100) {
86
92
  clearDelayedAutoStart(context.state);
@@ -224,8 +230,8 @@ function createServiceLifecycle(context) {
224
230
  scheduleAutoStart: (delayMs) => scheduleAutoStart(context, delayMs)
225
231
  };
226
232
  }
227
- function createP2PService(env) {
228
- const roomSessionOwner = new P2PRoomSessionOwner(env);
233
+ function createP2PService(env, options = {}) {
234
+ const roomSessionOwner = new P2PRoomSessionOwner(env, void 0, options);
229
235
  const state = {
230
236
  explicitDisconnectVeto: false,
231
237
  lifecycleClosed: false,
@@ -1,4 +1,4 @@
1
- export type { P2PCandidateSummary, P2PChangeRelay, P2PConfigurationExchange, P2PDiagnostics, P2PPeerConnectionMetrics, P2PPeerAdmission, P2PPeerDirectory, P2PServiceViews, P2PTargetedTransfer, P2PTransportLifecycle, P2PConnectionProbeAdmission, P2PConnectionProbeAdmissionResult, P2PConnectionProbeSettings, } from "./P2PService.js";
1
+ export type { P2PCandidateSummary, P2PChangeRelay, P2PConfigurationExchange, P2PDiagnostics, P2PPeerConnectionMetrics, P2PPeerAdmission, P2PPeerDirectory, P2PServiceViews, P2PTargetedTransfer, P2PTransportLifecycle, P2PConnectionProbeAdmission, P2PConnectionProbeAdmissionResult, P2PConnectionProbeSettings, P2PServiceOptions, PrepareP2PSettings, } from "./P2PService.js";
2
2
  export { ACTIVE_P2P_RELAY_BINDING_CONFLICT } from "./P2PService.js";
3
3
  export { useP2PReplicatorCommands } from "../replication/trystero/useP2PReplicatorCommands.js";
4
4
  export { useP2PReplicatorFeature } from "../replication/trystero/useP2PReplicatorFeature.js";
@@ -6,3 +6,5 @@ export type { OpenRebuildUIFactory, OpenReplicationUIFactory, } from "../replica
6
6
  export type { UseP2PReplicatorResult } from "../replication/trystero/UseP2PReplicatorResult.js";
7
7
  export type { P2PReplicationResult } from "../replication/trystero/TrysteroReplicator.js";
8
8
  export type { Advertisement } from "../replication/trystero/types.js";
9
+ export { P2P_ICE_SERVER_EXPIRY_MARGIN_MS, P2P_SETTINGS_PREPARATION_TIMEOUT_MS, } from "../replication/trystero/P2PRoomSessionOwner.js";
10
+ export type { P2PReplicatorFeatureOptions } from "../replication/trystero/useP2PReplicatorFeature.js";
package/dist/p2p/index.js CHANGED
@@ -1,8 +1,14 @@
1
1
  import { ACTIVE_P2P_RELAY_BINDING_CONFLICT } from "./P2PService.js";
2
2
  import { useP2PReplicatorCommands } from "../replication/trystero/useP2PReplicatorCommands.js";
3
3
  import { useP2PReplicatorFeature } from "../replication/trystero/useP2PReplicatorFeature.js";
4
+ import {
5
+ P2P_ICE_SERVER_EXPIRY_MARGIN_MS,
6
+ P2P_SETTINGS_PREPARATION_TIMEOUT_MS
7
+ } from "../replication/trystero/P2PRoomSessionOwner.js";
4
8
  export {
5
9
  ACTIVE_P2P_RELAY_BINDING_CONFLICT,
10
+ P2P_ICE_SERVER_EXPIRY_MARGIN_MS,
11
+ P2P_SETTINGS_PREPARATION_TIMEOUT_MS,
6
12
  useP2PReplicatorCommands,
7
13
  useP2PReplicatorFeature
8
14
  };
@@ -1,8 +1,16 @@
1
+ import { type P2PSyncSetting } from "../../common/types.js";
1
2
  import type { LiveSyncReplicatorEnv } from "../LiveSyncAbstractReplicator.js";
2
3
  import { P2PRoomSession } from "./P2PRoomSession.js";
3
4
  import type { ReplicatorHostEnv } from "./types.js";
4
5
  import { type P2PConnectionProbeAdmissionResult, type P2PConnectionProbeSettings } from "./P2PConnectionProbeAdmission.js";
5
6
  type P2PRoomSessionFactory = (env: ReplicatorHostEnv) => P2PRoomSession;
7
+ /** Prepare connection-only P2P settings for one room generation. */
8
+ export type PrepareP2PSettings = (settings: Readonly<P2PSyncSetting>, signal: AbortSignal) => Promise<P2PSyncSetting>;
9
+ export interface P2PRoomSessionOwnerOptions {
10
+ readonly prepareP2PSettings?: PrepareP2PSettings;
11
+ }
12
+ export declare const P2P_SETTINGS_PREPARATION_TIMEOUT_MS = 30000;
13
+ export declare const P2P_ICE_SERVER_EXPIRY_MARGIN_MS = 30000;
6
14
  /** Session operations shared by the stable service and its compatibility facade. */
7
15
  export interface P2PRoomSessionAccess {
8
16
  readonly currentSession: P2PRoomSession | undefined;
@@ -22,13 +30,16 @@ export type P2PPersistentRoomDemand = "explicit" | "automatic" | "rebuild-contin
22
30
  export declare class P2PRoomSessionOwner implements P2PRoomSessionAccess {
23
31
  private readonly env;
24
32
  private readonly createSession;
33
+ private readonly options;
25
34
  private current?;
26
35
  private lifecycleOperation;
27
36
  private readonly persistentDemands;
28
37
  private readonly finiteDemands;
29
38
  private readonly automationCoordinator;
30
39
  private activeBinding?;
31
- constructor(env: LiveSyncReplicatorEnv, createSession?: P2PRoomSessionFactory);
40
+ private pendingPreparation?;
41
+ private runtimeGeneration;
42
+ constructor(env: LiveSyncReplicatorEnv, createSession?: P2PRoomSessionFactory, options?: P2PRoomSessionOwnerOptions);
32
43
  get currentSession(): P2PRoomSession | undefined;
33
44
  get isConnected(): boolean;
34
45
  cancelActiveTransfers(): void;
@@ -52,6 +63,16 @@ export declare class P2PRoomSessionOwner implements P2PRoomSessionAccess {
52
63
  private enqueueLifecycleOperation;
53
64
  private closeTransport;
54
65
  private bindingsMatch;
66
+ private connectionSettingsRemainUsable;
67
+ private fencePreparationForCurrentSettings;
68
+ private invalidatePreparation;
69
+ private prepareConnectionSettings;
70
+ private prepareWithDeadline;
71
+ private copyPreparedIceServers;
72
+ private hasUsableIceServer;
73
+ private hasUsableTurnServer;
74
+ private withRoomOpenDeadline;
75
+ private assertReconciliationCurrent;
55
76
  /** Capture immutable session inputs while keeping policy live. */
56
77
  private getEffectiveBinding;
57
78
  /** Apply policy which can change without retiring the room session. */