@vrtmrz/livesync-commonlib 0.1.0-rc.1 → 0.1.0-rc.11

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 (141) hide show
  1. package/README.md +90 -20
  2. package/dist/API/DirectFileManipulatorV2.d.ts +3 -0
  3. package/dist/common/configForDoc.d.ts +1 -0
  4. package/dist/common/configForDoc.js +24 -7
  5. package/dist/common/models/setting.const.defaults.d.ts +19 -0
  6. package/dist/common/models/setting.const.defaults.js +21 -2
  7. package/dist/common/models/setting.const.preferred.js +1 -2
  8. package/dist/common/models/setting.lifecycle.d.ts +33 -0
  9. package/dist/common/models/setting.lifecycle.js +121 -0
  10. package/dist/common/models/setting.type.d.ts +10 -4
  11. package/dist/common/models/shared.definition.configNames.js +3 -3
  12. package/dist/common/models/tweak.definition.d.ts +1 -1
  13. package/dist/common/models/tweak.definition.js +1 -6
  14. package/dist/common/settingConstants.d.ts +3 -2
  15. package/dist/common/settingConstants.js +6 -6
  16. package/dist/common/types.d.ts +4 -2
  17. package/dist/common/types.js +16 -1
  18. package/dist/context.d.ts +2 -1
  19. package/dist/context.js +4 -0
  20. package/dist/interfaces/Confirm.d.ts +10 -2
  21. package/dist/interfaces/DatabaseFileAccess.d.ts +8 -0
  22. package/dist/interfaces/DatabaseRebuilder.d.ts +12 -2
  23. package/dist/interfaces/FileReflectionProvenance.d.ts +32 -0
  24. package/dist/interfaces/FileReflectionProvenance.js +25 -0
  25. package/dist/managers/ConflictManager.js +42 -9
  26. package/dist/managers/EntryManager/EntryManager.d.ts +1 -0
  27. package/dist/managers/EntryManager/EntryManager.js +5 -1
  28. package/dist/managers/EntryManager/EntryManagerImpls.d.ts +1 -0
  29. package/dist/managers/EntryManager/EntryManagerImpls.js +29 -1
  30. package/dist/platform/node/storage.d.ts +10 -1
  31. package/dist/platform/node/storage.js +86 -25
  32. package/dist/pouchdb/LiveSyncLocalDB.d.ts +1 -0
  33. package/dist/pouchdb/LiveSyncLocalDB.js +3 -0
  34. package/dist/remoteConfigurations.d.ts +9 -0
  35. package/dist/remoteConfigurations.js +14 -0
  36. package/dist/replication/LiveSyncAbstractReplicator.d.ts +3 -3
  37. package/dist/replication/couchdb/LiveSyncReplicator.d.ts +0 -1
  38. package/dist/replication/couchdb/LiveSyncReplicator.js +0 -3
  39. package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +0 -1
  40. package/dist/replication/journal/LiveSyncJournalReplicator.js +0 -3
  41. package/dist/replication/trystero/LiveSyncTrysteroReplicator.d.ts +5 -3
  42. package/dist/replication/trystero/LiveSyncTrysteroReplicator.js +42 -30
  43. package/dist/replication/trystero/P2PReplicatorCore.d.ts +2 -6
  44. package/dist/replication/trystero/P2PReplicatorCore.js +16 -86
  45. package/dist/replication/trystero/TrysteroReplicator.d.ts +2 -1
  46. package/dist/replication/trystero/TrysteroReplicator.js +10 -4
  47. package/dist/replication/trystero/TrysteroReplicatorP2PServer.d.ts +4 -5
  48. package/dist/replication/trystero/TrysteroReplicatorP2PServer.js +22 -30
  49. package/dist/replication/trystero/UseP2PReplicatorResult.d.ts +10 -2
  50. package/dist/replication/trystero/addP2PEventHandlers.d.ts +4 -2
  51. package/dist/replication/trystero/addP2PEventHandlers.js +9 -10
  52. package/dist/replication/trystero/types.d.ts +2 -0
  53. package/dist/replication/trystero/useP2PReplicatorCommands.d.ts +1 -1
  54. package/dist/replication/trystero/useP2PReplicatorCommands.js +3 -1
  55. package/dist/replication/trystero/useP2PReplicatorFeature.js +25 -15
  56. package/dist/rpc/RpcRoom.d.ts +1 -1
  57. package/dist/rpc/RpcRoom.js +22 -16
  58. package/dist/rpc/transports/TrysteroTransport.d.ts +2 -0
  59. package/dist/rpc/transports/TrysteroTransport.js +24 -17
  60. package/dist/rpc/transports/trysteroRoomEvents.d.ts +14 -0
  61. package/dist/rpc/transports/trysteroRoomEvents.js +34 -0
  62. package/dist/serviceFeatures/checkRemoteSize.d.ts +5 -3
  63. package/dist/serviceFeatures/checkRemoteSize.js +51 -11
  64. package/dist/serviceFeatures/remoteConfig.d.ts +37 -1
  65. package/dist/serviceFeatures/remoteConfig.js +90 -0
  66. package/dist/serviceModules/Rebuilder.d.ts +3 -6
  67. package/dist/serviceModules/Rebuilder.js +31 -22
  68. package/dist/serviceModules/ServiceDatabaseFileAccessBase.d.ts +5 -0
  69. package/dist/serviceModules/ServiceDatabaseFileAccessBase.js +69 -13
  70. package/dist/serviceModules/ServiceFileHandlerBase.d.ts +20 -0
  71. package/dist/serviceModules/ServiceFileHandlerBase.js +224 -40
  72. package/dist/services/BrowserServices.d.ts +2 -0
  73. package/dist/services/BrowserServices.js +4 -1
  74. package/dist/services/HeadlessServices.d.ts +2 -0
  75. package/dist/services/HeadlessServices.js +2 -1
  76. package/dist/services/base/CommonlibMessages.d.ts +318 -0
  77. package/dist/services/base/CommonlibMessages.js +313 -0
  78. package/dist/services/base/IService.d.ts +13 -6
  79. package/dist/services/base/KeyValueDBService.d.ts +1 -1
  80. package/dist/services/base/KeyValueDBService.js +3 -1
  81. package/dist/services/base/MessageTranslator.d.ts +5 -2
  82. package/dist/services/base/MessageTranslator.js +21 -0
  83. package/dist/services/base/ReplicationService.d.ts +4 -2
  84. package/dist/services/base/ReplicationService.js +11 -16
  85. package/dist/services/base/ServiceBase.d.ts +1 -1
  86. package/dist/services/base/ServiceBase.js +2 -4
  87. package/dist/services/base/SettingService.d.ts +17 -9
  88. package/dist/services/base/SettingService.js +38 -19
  89. package/dist/services/implements/headless/HeadlessAPIService.d.ts +3 -3
  90. package/dist/services/implements/headless/HeadlessAPIService.js +2 -2
  91. package/dist/services/implements/injectable/InjectableTweakValueService.d.ts +3 -3
  92. package/dist/settings.d.ts +10 -0
  93. package/dist/settings.js +32 -0
  94. package/dist/worker/bgWorker.js +1 -1
  95. package/docs/conflict-resolution.md +161 -0
  96. package/docs/development.md +106 -7
  97. package/docs/p2p-transport-lifecycle.md +62 -0
  98. package/docs/platform-storage.md +4 -0
  99. package/docs/proven-in-use.md +65 -0
  100. package/docs/releasing.md +26 -6
  101. package/docs/remote-configurations.md +63 -0
  102. package/docs/settings-lifecycle.md +85 -0
  103. package/package.json +17 -27
  104. package/dist/common/i18n.d.ts +0 -18
  105. package/dist/common/i18n.js +0 -7193
  106. package/dist/common/messages/combinedMessages.dev.d.ts +0 -7
  107. package/dist/common/messages/combinedMessages.dev.js +0 -44
  108. package/dist/common/messages/combinedMessages.prod.d.ts +0 -9264
  109. package/dist/common/messages/combinedMessages.prod.js +0 -9271
  110. package/dist/common/messages/de.d.ts +0 -297
  111. package/dist/common/messages/de.js +0 -7
  112. package/dist/common/messages/def.d.ts +0 -1119
  113. package/dist/common/messages/def.js +0 -7
  114. package/dist/common/messages/es.d.ts +0 -689
  115. package/dist/common/messages/es.js +0 -7
  116. package/dist/common/messages/fr.d.ts +0 -582
  117. package/dist/common/messages/fr.js +0 -7
  118. package/dist/common/messages/he.d.ts +0 -583
  119. package/dist/common/messages/he.js +0 -7
  120. package/dist/common/messages/ja.d.ts +0 -838
  121. package/dist/common/messages/ja.js +0 -7
  122. package/dist/common/messages/ko.d.ts +0 -800
  123. package/dist/common/messages/ko.js +0 -7
  124. package/dist/common/messages/ru.d.ts +0 -858
  125. package/dist/common/messages/ru.js +0 -7
  126. package/dist/common/messages/zh-tw.d.ts +0 -384
  127. package/dist/common/messages/zh-tw.js +0 -7
  128. package/dist/common/messages/zh.d.ts +0 -1093
  129. package/dist/common/messages/zh.js +0 -7
  130. package/dist/common/messagesJson/de.json +0 -295
  131. package/dist/common/messagesJson/en.json +0 -1117
  132. package/dist/common/messagesJson/es.json +0 -687
  133. package/dist/common/messagesJson/fr.json +0 -580
  134. package/dist/common/messagesJson/he.json +0 -581
  135. package/dist/common/messagesJson/ja.json +0 -836
  136. package/dist/common/messagesJson/ko.json +0 -798
  137. package/dist/common/messagesJson/ru.json +0 -856
  138. package/dist/common/messagesJson/zh-tw.json +0 -382
  139. package/dist/common/messagesJson/zh.json +0 -1091
  140. package/dist/common/rosetta.d.ts +0 -42
  141. package/dist/common/rosetta.js +0 -57
package/README.md CHANGED
@@ -1,37 +1,87 @@
1
1
  # Self-hosted LiveSync Commonlib
2
2
 
3
- Commonlib provides the shared data, storage, and replication primitives used by Self-hosted LiveSync, its CLI, Webapp, and WebPeer.
3
+ Commonlib is the ESM package which provides shared data, storage, replication, and host-context primitives for Self-hosted LiveSync, its CLI, Webapp, and WebPeer.
4
4
 
5
- The package is pre-1.0. Pin an exact reviewed version, and treat `compat/*` subpaths as migration-only interfaces. A source-directory import, Git submodule, or consumer-defined TypeScript alias is not a supported package boundary.
5
+ This package is primarily for maintainers of those clients and for integrations which deliberately reuse a reviewed Commonlib contract. It is not yet a general-purpose LiveSync SDK: the package does not provide a stable end-to-end client factory or own host UI, permissions, credentials, process lifecycle, or application persistence.
6
+
7
+ > [!IMPORTANT]
8
+ > **Package migration in progress.** Commonlib is moving from a source-directory and Git-submodule dependency to a compiled npm package as part of Self-hosted LiveSync's Community directory review, reusable package boundary, and 1.0 preparation work.
9
+ >
10
+ > Existing contributions remain welcome and the normal contribution process has not changed. Pull requests which touch packaging, host lifecycle, or compatibility boundaries may need coordination or a rebase while this boundary settles; maintainers will identify affected work rather than asking contributors to adopt a new process pre-emptively.
11
+
12
+ ## Status and versioning
13
+
14
+ The package is pre-1.0. Pin an exact reviewed version, and review changes before upgrading. An entry in the package export map means that the import is deliberate and package-tested; it does not make every semantic detail of that entry a final 1.0 guarantee.
15
+
16
+ Treat every `compat/*` subpath as a migration-only interface. A source-directory import, Git submodule, or consumer-defined TypeScript alias is not a supported package boundary.
17
+
18
+ The package is ESM-only and declares Node 20 or later. Browser entry points are built with browser conditions, but the host remains responsible for checking the availability of browser capabilities such as the File System Access API.
6
19
 
7
20
  ## Package entry points
8
21
 
9
- - `@vrtmrz/livesync-commonlib` contains the deliberately small root API.
10
- - `@vrtmrz/livesync-commonlib/context` contains host-context and translation injection contracts.
11
- - `@vrtmrz/livesync-commonlib/browser` contains browser-only adapters, including rooted File System Access API storage.
12
- - `@vrtmrz/livesync-commonlib/node` contains Node-only capabilities and rooted storage.
13
- - `@vrtmrz/livesync-commonlib/rpc` contains the focused RPC API.
14
- - `@vrtmrz/livesync-commonlib/compat/*` contains temporary explicit exports required by existing Self-hosted LiveSync consumers.
22
+ | Entry point | Intended use | Current contract status |
23
+ | -------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
24
+ | `@vrtmrz/livesync-commonlib` | `DirectFileManipulator` for integrations which access CouchDB directly | Deliberately small, but its high-level lifecycle and file-operation semantics are not final |
25
+ | `@vrtmrz/livesync-commonlib/context` | Instance-owned events, translation injection, and host-neutral standard-I/O types | Focused, package-tested pre-1.0 contract |
26
+ | `@vrtmrz/livesync-commonlib/browser` | Rooted File System Access API storage | Focused, package-tested pre-1.0 contract |
27
+ | `@vrtmrz/livesync-commonlib/node` | Rooted Node storage, Node standard I/O, and selected Node capabilities | Supported platform façade; keeps Node-only dependencies behind an explicit boundary |
28
+ | `@vrtmrz/livesync-commonlib/rpc` | Existing LiveSync RPC and PouchDB bridge migration | Transitional only; not part of the stable Commonlib 1.0 contract |
29
+ | `@vrtmrz/livesync-commonlib/remote-configurations` | Multiple-remote profile creation, naming, and selection | Focused, package-tested pre-1.0 contract |
30
+ | `@vrtmrz/livesync-commonlib/settings` | New-Vault defaults, stored-setting fallbacks, and settings migration results | Focused, package-tested pre-1.0 contract |
31
+ | `@vrtmrz/livesync-commonlib/compat/*` | Exact legacy imports still required by existing clients | Migration-only; paths may be removed as consumers migrate |
32
+ | `@vrtmrz/livesync-commonlib/package.json` | Package metadata for tooling | Metadata export, not a runtime API |
15
33
 
16
34
  Import only the entry point required by the current runtime. Browser code must not import the Node entry.
17
35
 
36
+ The Node and browser entries are platform façades. They keep platform-specific adapters and capabilities behind package-owned boundaries, so a consumer does not have to reproduce those imports or their build and review exceptions. The Node entry's selected built-in-module exports are deliberate parts of that façade. Package checks ensure that the root, context, and browser entries do not load the Node implementation.
37
+
38
+ The RPC entry exists only while the maintained LiveSync P2P composition depends on the current implementation. Its draft wire protocol and PouchDB bridge are not stable Commonlib 1.0 contracts. RPC is planned to move to Fancy Kit as an independently owned rewrite; new Commonlib consumers should not adopt the transitional entry.
39
+
40
+ ## Host context and initialisation
41
+
42
+ One service context belongs to one independently composed Commonlib client. It owns that client's event hub and translator. Creating a context without options creates a fresh event hub and renders Commonlib's typed canonical English messages. A host may inject a translator to provide another language:
43
+
44
+ ```ts
45
+ import {
46
+ createLiveSyncEventHub,
47
+ createServiceContext,
48
+ type MessageTranslator,
49
+ } from "@vrtmrz/livesync-commonlib/context";
50
+
51
+ const events = createLiveSyncEventHub();
52
+ declare const hostTranslations: { translate: MessageTranslator };
53
+ const translate: MessageTranslator = (key, parameters) => {
54
+ return hostTranslations.translate(key, parameters);
55
+ };
56
+
57
+ const context = createServiceContext({ events, translate });
58
+ ```
59
+
60
+ The host may instead extend `ServiceContext` with platform-specific capabilities. Pass the same context instance to every service in one composition so that they share the intended event channel and translator. Create a separate context for a client which must be isolated.
61
+
62
+ Context construction does not select storage, connect to a database, start replication, acquire browser permission, or install process-wide state. The host separately owns platform-resource acquisition, adapter construction, and the lifecycle order of its particular service composition.
63
+
64
+ After the Self-hosted LiveSync 1.0 work, the planned high-level composition is a narrow `createLiveSyncFileClient` factory for direct database integrations, not a general factory which exposes Commonlib's internal Service Hub. It will resolve only after its private context, services, database, and protocol settings are ready, and its returned file client will own its watches, reconnect work, and orderly disposal. Obsidian, CLI, Webapp, and other full hosts retain their host-specific compositions.
65
+
66
+ The factory is not implemented yet. Until it is available, service implementations exposed under `compat/*` retain their owning client's lifecycle rather than becoming a general public composition API.
67
+
18
68
  ## Rooted storage
19
69
 
20
70
  Both platform storage factories receive an existing root from the host. They never select a directory, request browser permission, infer a process directory, or persist a handle.
21
71
 
22
72
  ```ts
23
- import { createFileSystemAccessStorage } from '@vrtmrz/livesync-commonlib/browser';
73
+ import { createFileSystemAccessStorage } from "@vrtmrz/livesync-commonlib/browser";
24
74
 
25
75
  declare const authorisedRoot: FileSystemDirectoryHandle;
26
76
  const storage = createFileSystemAccessStorage({ rootHandle: authorisedRoot });
27
- await storage.write('notes/example.md', '# Example');
77
+ await storage.write("notes/example.md", "# Example");
28
78
  ```
29
79
 
30
80
  ```ts
31
- import { createNodeStorage } from '@vrtmrz/livesync-commonlib/node';
81
+ import { createNodeStorage } from "@vrtmrz/livesync-commonlib/node";
32
82
 
33
- const storage = createNodeStorage({ rootPath: '/srv/livesync' });
34
- await storage.write('notes/example.md', '# Example');
83
+ const storage = createNodeStorage({ rootPath: "/srv/livesync" });
84
+ await storage.write("notes/example.md", "# Example");
35
85
  ```
36
86
 
37
87
  See [the platform storage contract](docs/platform-storage.md) for path, ownership, and permission details.
@@ -41,22 +91,42 @@ See [the platform storage contract](docs/platform-storage.md) for path, ownershi
41
91
  CLI hosts can inject the platform-neutral `StandardIo` contract from the context entry. The Node entry supplies the concrete stream adapter. This keeps command input and protocol output testable without treating logging or process lifecycle as standard I/O.
42
92
 
43
93
  ```ts
44
- import type { StandardIo } from '@vrtmrz/livesync-commonlib/context';
45
- import { createNodeStandardIo } from '@vrtmrz/livesync-commonlib/node';
94
+ import type { StandardIo } from "@vrtmrz/livesync-commonlib/context";
95
+ import { createNodeStandardIo } from "@vrtmrz/livesync-commonlib/node";
46
96
 
47
97
  const standardIo: StandardIo = createNodeStandardIo();
48
- standardIo.writeStdout('ready\n');
98
+ standardIo.writeStdout("ready\n");
49
99
  ```
50
100
 
51
101
  See [the standard-I/O contract](docs/platform-standard-io.md) for host composition and test-double examples.
52
102
 
53
- ## API status
103
+ ## Settings lifecycle
104
+
105
+ New-Vault recommendations are deliberately separate from the conservative values used to complete older stored settings. The focused settings entry also reports migrations and safety-review requirements without changing a user's synchronisation choices or persisting a device-local acknowledgement.
106
+
107
+ See [the settings lifecycle guide](docs/settings-lifecycle.md) before initialising, importing, resetting, or migrating settings.
108
+
109
+ ## Remote connection profiles
110
+
111
+ New profile-management code can create or update a CouchDB, Object Storage, or P2P connection through the focused remote-configurations entry. Profile IDs provide identity, display names remain editable presentation, and the active selection is explicit. Older flat connection fields remain a runtime projection and an import-compatibility boundary rather than the preferred persistence model.
112
+
113
+ See [the remote configuration profile guide](docs/remote-configurations.md) for creation, activation, P2P selection, legacy import, persistence, and verification responsibilities.
114
+
115
+ ## Contract scope
116
+
117
+ The package is currently an infrastructure and compatibility boundary. The context, rooted-storage, standard-I/O, and revision-tree safety contracts have focused cross-platform, instance-isolation, or real-PouchDB tests. Platform details which cannot be shared, such as file timestamp fidelity and browser permission handling, remain host concerns and are documented separately.
118
+
119
+ `DirectFileManipulator` is useful for existing integrations, but its enumeration, watch ownership, failure, conflict, concurrency, readiness, and disposal semantics are not a stable high-level SDK contract. It is the migration source for the planned file client, not the shape to preserve as the new API.
120
+
121
+ The accepted replacement direction is an asynchronously created file client with stable `list`, `get`, `put`, `delete`, `watch`, and `close` operations. The operation result, conflict, concurrency, watch checkpoint, retry, and error contracts still require focused decisions and tests before that client can be published.
122
+
123
+ Package developers should read [the developer guide](docs/development.md). The focused contracts are described in [the storage guide](docs/platform-storage.md), [the standard-I/O guide](docs/platform-standard-io.md), [the settings lifecycle guide](docs/settings-lifecycle.md), [the remote configuration profile guide](docs/remote-configurations.md), and [the conflict-resolution guide](docs/conflict-resolution.md).
54
124
 
55
- The package is currently an infrastructure and compatibility boundary. `DirectFileManipulator` is useful for existing integrations, but its enumeration, watch ownership, failure, conflict, concurrency, and disposal semantics are not yet a stable high-level SDK contract.
125
+ ## Proven in maintained hosts
56
126
 
57
- TODO: define and document the first high-level client façade before declaring list, get, put, delete, watch, and close behaviour stable.
127
+ The package boundary is exercised by Self-hosted LiveSync's Obsidian plug-in, CLI, Webapp, and WebPeer compositions. Their validation is not uniform: the Obsidian plug-in and CLI have the strongest real-runtime and cross-host evidence, while the Webapp and WebPeer currently provide focused adapter, type-check, and build evidence.
58
128
 
59
- Development and package validation are documented in [the developer guide](docs/development.md).
129
+ See [Proven in maintained hosts](docs/proven-in-use.md) for the actual composition points, downstream tests, and remaining limits. The examples deliberately separate focused public entries which a new consumer may copy from `compat/*` imports which exist to migrate the current application.
60
130
 
61
131
  ## Licence
62
132
 
@@ -27,6 +27,9 @@ export type DirectFileManipulatorOptions = {
27
27
  enableChunkSplitterV2?: boolean;
28
28
  enableCompression?: boolean;
29
29
  handleFilenameCaseSensitive?: boolean;
30
+ /**
31
+ * @deprecated Chunk revisions are always derived from their content.
32
+ */
30
33
  doNotUseFixedRevisionForChunks?: boolean;
31
34
  chunkSplitterVersion?: ChunkSplitterVersion;
32
35
  E2EEAlgorithm?: E2EEAlgorithm;
@@ -51,6 +51,7 @@ export declare const DoctorRegulationV0_24_16: DoctorRegulation;
51
51
  export declare const DoctorRegulationV0_24_30: DoctorRegulation;
52
52
  export declare const DoctorRegulationV0_25_0: DoctorRegulation;
53
53
  export declare const DoctorRegulationV0_25_27: DoctorRegulation;
54
+ export declare const DoctorRegulationV1_0_0: DoctorRegulation;
54
55
  export declare const DoctorRegulation: DoctorRegulation;
55
56
  export declare function checkUnsuitableValues(setting: Partial<ObsidianLiveSyncSettings>, regulation?: DoctorRegulation): DoctorRegulation;
56
57
  export declare const RebuildOptions: {
@@ -1,7 +1,12 @@
1
1
  import { isCloudantURI } from "../pouchdb/utils_couchdb.js";
2
2
  import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, Logger } from "./logger.js";
3
3
  import { getConfName } from "./settingConstants.js";
4
- import { ChunkAlgorithmNames, E2EEAlgorithmNames, E2EEAlgorithms } from "./types.js";
4
+ import {
5
+ ChunkAlgorithmNames,
6
+ E2EEAlgorithmNames,
7
+ E2EEAlgorithms,
8
+ REMOTE_COUCHDB
9
+ } from "./types.js";
5
10
  var ConditionType = /* @__PURE__ */ ((ConditionType2) => {
6
11
  ConditionType2["PLATFORM_CASE_INSENSITIVE"] = "platform-case-insensitive";
7
12
  ConditionType2["PLATFORM_CASE_SENSITIVE"] = "platform-case-sensitive";
@@ -20,12 +25,12 @@ const DoctorRegulationV0_24_16 = {
20
25
  rules: {
21
26
  sendChunksBulk: {
22
27
  value: false,
23
- reason: "This is an obsolete setting and we should not enable this no more",
28
+ reason: "Automatic bulk chunk pre-send is obsolete and must remain disabled",
24
29
  level: 0 /* Must */
25
30
  },
26
31
  sendChunksBulkMaxSize: {
27
32
  value: 1,
28
- reason: "This is an obsolete setting and we should not enable this no more",
33
+ reason: "Use a conservative request size for the explicit manual chunk resend tool",
29
34
  level: 0 /* Must */
30
35
  },
31
36
  doNotUseFixedRevisionForChunks: {
@@ -90,7 +95,7 @@ const DoctorRegulationV0_24_30 = {
90
95
  value: 60,
91
96
  valueDisplay: "60 (detected on if less than 55)",
92
97
  level: 2 /* Recommended */,
93
- detectionFunc: (settings) => settings?.chunkSplitterVersion === "v3-rabin-karp" && !isCloudantURI(settings?.couchDB_URI || ""),
98
+ detectionFunc: (settings) => settings?.remoteType === REMOTE_COUCHDB && settings?.chunkSplitterVersion === "v3-rabin-karp" && !isCloudantURI(settings?.couchDB_URI || ""),
94
99
  reason: "With the V3 Rabin-Karp chunk splitter and Self-hosted CouchDB, the chunk size is set to 60 (means around 6MB) by default. This is in effect the maximum chunk size, which in practice is divided more finely."
95
100
  }
96
101
  }
@@ -111,10 +116,18 @@ const DoctorRegulationV0_25_27 = {
111
116
  version: "0.25.27",
112
117
  rules: {
113
118
  ...DoctorRegulationV0_25_0.rules,
114
- useIndexedDBAdapter: void 0
119
+ useIndexedDBAdapter: void 0,
120
+ doNotUseFixedRevisionForChunks: void 0
115
121
  }
116
122
  };
117
- const DoctorRegulation = DoctorRegulationV0_25_27;
123
+ const DoctorRegulationV1_0_0 = {
124
+ version: "1.0.0",
125
+ rules: {
126
+ ...DoctorRegulationV0_25_27.rules,
127
+ enableCompression: void 0
128
+ }
129
+ };
130
+ const DoctorRegulation = DoctorRegulationV1_0_0;
118
131
  function checkUnsuitableValues(setting, regulation = DoctorRegulation) {
119
132
  const result = {
120
133
  version: regulation.version,
@@ -263,7 +276,10 @@ async function performDoctorConsultation(env, settings, {
263
276
  }),
264
277
  options,
265
278
  {
266
- title: translate("Doctor.Dialogue.TitleFix", { current: `${++idx}`, total: `${issueItems.length}` }),
279
+ title: translate("Doctor.Dialogue.TitleFix", {
280
+ current: `${++idx}`,
281
+ total: `${issueItems.length}`
282
+ }),
267
283
  defaultAction: OPT_FIX
268
284
  }
269
285
  );
@@ -305,6 +321,7 @@ export {
305
321
  DoctorRegulationV0_24_30,
306
322
  DoctorRegulationV0_25_0,
307
323
  DoctorRegulationV0_25_27,
324
+ DoctorRegulationV1_0_0,
308
325
  RebuildOptions,
309
326
  RuleLevel,
310
327
  checkUnsuitableValues,
@@ -1,3 +1,22 @@
1
1
  import { type ObsidianLiveSyncSettings, type P2PSyncSetting } from "./setting.type.js";
2
2
  export declare const P2P_DEFAULT_SETTINGS: P2PSyncSetting;
3
+ /**
4
+ * Conservative fallback values used to complete stored settings.
5
+ *
6
+ * Keep these values compatible with existing installations. Defaults intended
7
+ * only for a newly created Vault belong in {@link NEW_VAULT_SETTINGS}.
8
+ */
9
+ export declare const SETTINGS_SCHEMA_DEFAULTS: ObsidianLiveSyncSettings;
10
+ /**
11
+ * Initial values for a Vault which has never stored Self-hosted LiveSync settings.
12
+ *
13
+ * This object is deliberately separate from schema fallbacks so that future
14
+ * releases can improve new-user defaults without changing existing choices.
15
+ */
16
+ export declare const NEW_VAULT_SETTINGS: ObsidianLiveSyncSettings;
17
+ /** Create an independently mutable copy of the current new-Vault settings. */
18
+ export declare function createNewVaultSettings(): ObsidianLiveSyncSettings;
19
+ /**
20
+ * Compatibility name for the conservative setting-schema fallbacks.
21
+ */
3
22
  export declare const DEFAULT_SETTINGS: ObsidianLiveSyncSettings;
@@ -1,4 +1,5 @@
1
1
  import { ChunkAlgorithms, CURRENT_SETTING_VERSION, E2EEAlgorithms, REMOTE_COUCHDB } from "./setting.const.js";
2
+ import { PREFERRED_BASE } from "./setting.const.preferred.js";
2
3
  import { AutoAccepting } from "./setting.type.js";
3
4
  const P2P_DEFAULT_SETTINGS = {
4
5
  P2P_Enabled: false,
@@ -22,7 +23,7 @@ const P2P_DEFAULT_SETTINGS = {
22
23
  P2P_turnCredential: "",
23
24
  P2P_useDiagRTC: false
24
25
  };
25
- const DEFAULT_SETTINGS = {
26
+ const SETTINGS_SCHEMA_DEFAULTS = {
26
27
  remoteType: REMOTE_COUCHDB,
27
28
  useCustomRequestHandler: false,
28
29
  couchDB_URI: "",
@@ -145,6 +146,7 @@ const DEFAULT_SETTINGS = {
145
146
  handleFilenameCaseSensitive: void 0,
146
147
  doNotUseFixedRevisionForChunks: true,
147
148
  showLongerLogInsideEditor: false,
149
+ /** @deprecated Retained for settings and Setup URI compatibility. */
148
150
  sendChunksBulk: false,
149
151
  sendChunksBulkMaxSize: 1,
150
152
  /**
@@ -179,7 +181,24 @@ const DEFAULT_SETTINGS = {
179
181
  maxMTimeForReflectEvents: 0,
180
182
  tweakModified: void 0
181
183
  };
184
+ const NEW_VAULT_SETTINGS = {
185
+ ...SETTINGS_SCHEMA_DEFAULTS,
186
+ ...PREFERRED_BASE,
187
+ remoteConfigurations: {},
188
+ pluginSyncExtendedSetting: {}
189
+ };
190
+ function createNewVaultSettings() {
191
+ return {
192
+ ...NEW_VAULT_SETTINGS,
193
+ remoteConfigurations: {},
194
+ pluginSyncExtendedSetting: {}
195
+ };
196
+ }
197
+ const DEFAULT_SETTINGS = SETTINGS_SCHEMA_DEFAULTS;
182
198
  export {
183
199
  DEFAULT_SETTINGS,
184
- P2P_DEFAULT_SETTINGS
200
+ NEW_VAULT_SETTINGS,
201
+ P2P_DEFAULT_SETTINGS,
202
+ SETTINGS_SCHEMA_DEFAULTS,
203
+ createNewVaultSettings
185
204
  };
@@ -2,7 +2,6 @@ import { E2EEAlgorithms } from "./setting.const.js";
2
2
  const PREFERRED_BASE = {
3
3
  syncMaxSizeInMB: 50,
4
4
  chunkSplitterVersion: "v3-rabin-karp",
5
- doNotUseFixedRevisionForChunks: false,
6
5
  usePluginSyncV2: true,
7
6
  handleFilenameCaseSensitive: false,
8
7
  E2EEAlgorithm: E2EEAlgorithms.V2
@@ -16,7 +15,7 @@ const PREFERRED_SETTING_CLOUDANT = {
16
15
  };
17
16
  const PREFERRED_SETTING_SELF_HOSTED = {
18
17
  ...PREFERRED_BASE,
19
- customChunkSize: 50,
18
+ customChunkSize: 60,
20
19
  sendChunksBulkMaxSize: 1,
21
20
  concurrencyOfReadChunksOnline: 30,
22
21
  minimumIntervalOfReadChunksOnline: 25
@@ -0,0 +1,33 @@
1
+ import type { ObsidianLiveSyncSettings } from "./setting.type.js";
2
+ export declare const SettingsMigrationReviewCodes: {
3
+ readonly LegacyUpdatePending: "legacy-update-review-pending";
4
+ readonly FutureSchema: "settings-schema-newer-than-runtime";
5
+ /** @deprecated Missing legacy values are now normalised to false and do not emit this reason. */
6
+ readonly FilenameCaseSensitivityUnresolved: "filename-case-sensitivity-unresolved";
7
+ };
8
+ export type SettingsMigrationReviewCode = (typeof SettingsMigrationReviewCodes)[keyof typeof SettingsMigrationReviewCodes];
9
+ export interface SettingsMigrationReviewReason {
10
+ code: SettingsMigrationReviewCode;
11
+ fromVersion: number;
12
+ toVersion: number;
13
+ }
14
+ export interface SettingsMigrationState {
15
+ sourceVersion: number;
16
+ targetVersion: number;
17
+ isNewVault: boolean;
18
+ isFromFutureSchema: boolean;
19
+ changed: boolean;
20
+ requiresSyncReview: boolean;
21
+ reviewReasons: readonly SettingsMigrationReviewReason[];
22
+ }
23
+ export interface PreparedSettings extends SettingsMigrationState {
24
+ settings: ObsidianLiveSyncSettings;
25
+ }
26
+ /**
27
+ * Completes and migrates raw settings before services consume them.
28
+ *
29
+ * Stored settings use conservative schema fallbacks. A blank store uses the
30
+ * independently maintained new-Vault defaults. Explicit stored values always
31
+ * take precedence over both sets of defaults.
32
+ */
33
+ export declare function prepareSettingsForLoad(storedSettings: Partial<ObsidianLiveSyncSettings> | undefined): PreparedSettings;
@@ -0,0 +1,121 @@
1
+ import { createNewVaultSettings, SETTINGS_SCHEMA_DEFAULTS } from "./setting.const.defaults.js";
2
+ import {
3
+ CURRENT_SETTING_VERSION,
4
+ SETTING_VERSION_INITIAL,
5
+ SETTING_VERSION_SUPPORT_CASE_INSENSITIVE
6
+ } from "./setting.const.js";
7
+ const SettingsMigrationReviewCodes = {
8
+ LegacyUpdatePending: "legacy-update-review-pending",
9
+ FutureSchema: "settings-schema-newer-than-runtime",
10
+ /** @deprecated Missing legacy values are now normalised to false and do not emit this reason. */
11
+ FilenameCaseSensitivityUnresolved: "filename-case-sensitivity-unresolved"
12
+ };
13
+ const SETTINGS_MIGRATIONS = [
14
+ {
15
+ toVersion: SETTING_VERSION_SUPPORT_CASE_INSENSITIVE,
16
+ apply: (settings) => settings,
17
+ reviewReason: (settings, fromVersion) => settings.versionUpFlash ? {
18
+ code: SettingsMigrationReviewCodes.LegacyUpdatePending,
19
+ fromVersion,
20
+ toVersion: SETTING_VERSION_SUPPORT_CASE_INSENSITIVE
21
+ } : void 0
22
+ }
23
+ ];
24
+ function cloneSettings(source, fallbacks) {
25
+ return {
26
+ ...fallbacks,
27
+ ...source,
28
+ remoteConfigurations: Object.fromEntries(
29
+ Object.entries(source.remoteConfigurations ?? fallbacks.remoteConfigurations).map(([id, config]) => [
30
+ id,
31
+ { ...config }
32
+ ])
33
+ ),
34
+ pluginSyncExtendedSetting: {
35
+ ...fallbacks.pluginSyncExtendedSetting,
36
+ ...source.pluginSyncExtendedSetting
37
+ }
38
+ };
39
+ }
40
+ function storedSettingVersion(settings) {
41
+ const version = settings.settingVersion;
42
+ return typeof version === "number" && Number.isSafeInteger(version) && version >= SETTING_VERSION_INITIAL ? version : SETTING_VERSION_INITIAL;
43
+ }
44
+ function isBlankSettings(settings) {
45
+ return settings === void 0 || Object.keys(settings).length === 0;
46
+ }
47
+ function inferLegacyConfiguredState(settings) {
48
+ const completed = { ...SETTINGS_SCHEMA_DEFAULTS, ...settings };
49
+ return JSON.stringify(completed) !== JSON.stringify(SETTINGS_SCHEMA_DEFAULTS);
50
+ }
51
+ function prepareSettingsForLoad(storedSettings) {
52
+ if (isBlankSettings(storedSettings)) {
53
+ return {
54
+ settings: createNewVaultSettings(),
55
+ sourceVersion: CURRENT_SETTING_VERSION,
56
+ targetVersion: CURRENT_SETTING_VERSION,
57
+ isNewVault: true,
58
+ isFromFutureSchema: false,
59
+ changed: false,
60
+ requiresSyncReview: false,
61
+ reviewReasons: []
62
+ };
63
+ }
64
+ const source = { ...storedSettings };
65
+ const sourceVersion = storedSettingVersion(source);
66
+ if (sourceVersion > CURRENT_SETTING_VERSION) {
67
+ const reviewReasons2 = [
68
+ {
69
+ code: SettingsMigrationReviewCodes.FutureSchema,
70
+ fromVersion: sourceVersion,
71
+ toVersion: CURRENT_SETTING_VERSION
72
+ }
73
+ ];
74
+ return {
75
+ settings: cloneSettings(source, SETTINGS_SCHEMA_DEFAULTS),
76
+ sourceVersion,
77
+ targetVersion: sourceVersion,
78
+ isNewVault: false,
79
+ isFromFutureSchema: true,
80
+ changed: false,
81
+ requiresSyncReview: true,
82
+ reviewReasons: reviewReasons2
83
+ };
84
+ }
85
+ let migrated = source;
86
+ let targetVersion = sourceVersion;
87
+ const reviewReasons = [];
88
+ for (const migration of SETTINGS_MIGRATIONS) {
89
+ if (targetVersion >= migration.toVersion) continue;
90
+ const reason = migration.reviewReason?.(migrated, targetVersion);
91
+ if (reason) reviewReasons.push(reason);
92
+ migrated = migration.apply(migrated);
93
+ targetVersion = migration.toVersion;
94
+ migrated.settingVersion = targetVersion;
95
+ }
96
+ if (targetVersion !== CURRENT_SETTING_VERSION) {
97
+ throw new Error(
98
+ `No setting migration reaches schema ${CURRENT_SETTING_VERSION} from schema ${sourceVersion}.`
99
+ );
100
+ }
101
+ const normalisedConfiguredState = migrated.isConfigured === void 0;
102
+ if (normalisedConfiguredState) {
103
+ migrated = { ...migrated, isConfigured: inferLegacyConfiguredState(source) };
104
+ }
105
+ const normalisedFilenameCase = typeof migrated.handleFilenameCaseSensitive !== "boolean";
106
+ if (normalisedFilenameCase) migrated = { ...migrated, handleFilenameCaseSensitive: false };
107
+ return {
108
+ settings: cloneSettings(migrated, SETTINGS_SCHEMA_DEFAULTS),
109
+ sourceVersion,
110
+ targetVersion,
111
+ isNewVault: false,
112
+ isFromFutureSchema: false,
113
+ changed: sourceVersion !== targetVersion || normalisedConfiguredState || normalisedFilenameCase,
114
+ requiresSyncReview: reviewReasons.length > 0,
115
+ reviewReasons
116
+ };
117
+ }
118
+ export {
119
+ SettingsMigrationReviewCodes,
120
+ prepareSettingsForLoad
121
+ };
@@ -1,7 +1,8 @@
1
1
  import type { ChunkAlgorithms, E2EEAlgorithms, HashAlgorithms, MODE_AUTOMATIC, MODE_PAUSED, MODE_SELECTIVE, MODE_SHINY, RemoteTypes } from "./setting.const.js";
2
- import type { I18N_LANGS } from "../rosetta.js";
3
2
  import type { CustomRegExpSourceList } from "./shared.type.util.js";
4
3
  import type { JWTAlgorithm } from "./auth.type.js";
4
+ /** Display languages recognised by the current LiveSync settings schema. */
5
+ export type I18N_LANGS = "" | "def" | "de" | "es" | "fr" | "he" | "ja" | "ko" | "ru" | "zh" | "zh-tw";
5
6
  /**
6
7
  * Represents the connection details required to connect to a CouchDB instance.
7
8
  */
@@ -599,7 +600,10 @@ interface ChunkSettings {
599
600
  */
600
601
  enableChunkSplitterV2: boolean;
601
602
  /**
602
- * Flag indicating whether to avoid using a fixed revision for chunks.
603
+ * Retained for stored-setting and Setup URI compatibility. Chunk revisions
604
+ * are always derived from their content, irrespective of this value.
605
+ *
606
+ * @deprecated This behaviour is no longer configurable.
603
607
  */
604
608
  doNotUseFixedRevisionForChunks: boolean;
605
609
  /**
@@ -668,11 +672,13 @@ interface ObsoleteRemoteDBSettings {
668
672
  */
669
673
  disableRequestURI: boolean;
670
674
  /**
671
- * Indicates whether to send data in bulk chunks.
675
+ * Retained for settings and Setup URI compatibility.
676
+ * Automatic bulk chunk pre-send is no longer supported.
677
+ * @deprecated
672
678
  */
673
679
  sendChunksBulk: boolean;
674
680
  /**
675
- * The maximum size of the bulk chunks to be sent.
681
+ * The maximum request size used by the explicit manual chunk resend tool, in MB.
676
682
  */
677
683
  sendChunksBulkMaxSize: number;
678
684
  /**
@@ -18,7 +18,7 @@ const configurationNames = {
18
18
  },
19
19
  enableCompression: {
20
20
  name: "Data Compression",
21
- status: "EXPERIMENTAL"
21
+ level: LEVEL_ADVANCED
22
22
  },
23
23
  useEden: {
24
24
  name: "Incubate Chunks in Document",
@@ -61,8 +61,8 @@ const configurationNames = {
61
61
  desc: "If this enabled, All files are handled as case-Sensitive (Previous behaviour)."
62
62
  },
63
63
  doNotUseFixedRevisionForChunks: {
64
- name: "Compute revisions for chunks (Previous behaviour)",
65
- desc: "If this enabled, all chunks will be stored with the revision made from its content. (Previous behaviour)"
64
+ name: "Content-derived chunk revisions (obsolete setting)",
65
+ desc: "Chunk revisions are always derived from their content. This stored key is retained only for compatibility."
66
66
  },
67
67
  useSegmenter: {
68
68
  name: "Use Segmented-splitter",
@@ -34,7 +34,7 @@ export declare const TweakValuesTemplate: {
34
34
  showOnlyIconsOnEditor?: boolean;
35
35
  hideFileWarningNotice?: boolean;
36
36
  networkWarningStyle?: "" | "icon" | "hidden";
37
- displayLanguage?: import("../rosetta.js").I18N_LANGS;
37
+ displayLanguage?: import("./setting.type.js").I18N_LANGS;
38
38
  trashInsteadDelete?: boolean;
39
39
  doNotDeleteFolder?: boolean;
40
40
  batchSave?: boolean;
@@ -16,7 +16,6 @@ const TweakValuesShouldMatchedTemplate = {
16
16
  maxAgeInEden: 10,
17
17
  usePluginSyncV2: false,
18
18
  handleFilenameCaseSensitive: false,
19
- doNotUseFixedRevisionForChunks: true,
20
19
  useSegmenter: false,
21
20
  E2EEAlgorithm: E2EEAlgorithms.V2,
22
21
  chunkSplitterVersion: ChunkAlgorithms.RabinKarp
@@ -28,10 +27,7 @@ const IncompatibleChanges = [
28
27
  "handleFilenameCaseSensitive"
29
28
  ];
30
29
  const CompatibleButLossyChanges = ["hashAlg", "customChunkSize", "chunkSplitterVersion"];
31
- const IncompatibleChangesInSpecificPattern = [
32
- { key: "doNotUseFixedRevisionForChunks", from: true, to: false, isRecommendation: true },
33
- { key: "doNotUseFixedRevisionForChunks", to: true, isRecommendation: false }
34
- ];
30
+ const IncompatibleChangesInSpecificPattern = [];
35
31
  const TweakValuesRecommendedTemplate = {
36
32
  useIgnoreFiles: false,
37
33
  useCustomRequestHandler: false,
@@ -49,7 +45,6 @@ const TweakValuesRecommendedTemplate = {
49
45
  enableChunkSplitterV2: false,
50
46
  usePluginSyncV2: true,
51
47
  handleFilenameCaseSensitive: false,
52
- doNotUseFixedRevisionForChunks: false,
53
48
  E2EEAlgorithm: E2EEAlgorithms.V2,
54
49
  chunkSplitterVersion: ChunkAlgorithms.RabinKarp
55
50
  };
@@ -1,3 +1,4 @@
1
+ import { type MessageTranslator } from "../services/base/MessageTranslator.js";
1
2
  import { type ConfigurationItem, type ObsidianLiveSyncSettings } from "./types.js";
2
3
  type ExtractPropertiesByType<T, U> = {
3
4
  [K in keyof T as T[K] extends U ? K : never]: T[K] extends U ? K : never;
@@ -43,7 +44,7 @@ export declare const AllSettingDefault: {
43
44
  showOnlyIconsOnEditor: boolean;
44
45
  hideFileWarningNotice: boolean;
45
46
  networkWarningStyle: "" | "icon" | "hidden";
46
- displayLanguage: import("./rosetta.js").I18N_LANGS;
47
+ displayLanguage: import("./models/setting.type.js").I18N_LANGS;
47
48
  trashInsteadDelete: boolean;
48
49
  doNotDeleteFolder: boolean;
49
50
  batchSave: boolean;
@@ -202,7 +203,7 @@ export type AllBooleanItemKey = FilterBooleanKeys<AllSettings>;
202
203
  export type AllSettingItemKey = AllStringItemKey | AllNumericItemKey | AllBooleanItemKey;
203
204
  export type ValueOf<T extends AllSettingItemKey> = T extends AllStringItemKey ? string : T extends AllNumericItemKey ? number : T extends AllBooleanItemKey ? boolean : AllSettings[T];
204
205
  export declare const SettingInformation: Partial<Record<keyof AllSettings, ConfigurationItem>>;
205
- type SettingLabelTranslator = (message: string) => string;
206
+ type SettingLabelTranslator = MessageTranslator;
206
207
  export declare function getConfig(key: AllSettingItemKey, translate?: SettingLabelTranslator): false | {
207
208
  name: string;
208
209
  desc?: string;