@vrtmrz/livesync-commonlib 0.1.0-rc.5 → 0.1.0-rc.6

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 (65) hide show
  1. package/README.md +11 -9
  2. package/dist/common/configForDoc.js +7 -2
  3. package/dist/common/models/setting.type.d.ts +2 -1
  4. package/dist/common/models/tweak.definition.d.ts +1 -1
  5. package/dist/common/settingConstants.d.ts +3 -2
  6. package/dist/common/settingConstants.js +2 -2
  7. package/dist/context.d.ts +2 -1
  8. package/dist/context.js +4 -0
  9. package/dist/replication/LiveSyncAbstractReplicator.d.ts +2 -1
  10. package/dist/serviceModules/Rebuilder.js +8 -3
  11. package/dist/services/BrowserServices.d.ts +2 -0
  12. package/dist/services/BrowserServices.js +4 -1
  13. package/dist/services/HeadlessServices.d.ts +2 -0
  14. package/dist/services/HeadlessServices.js +2 -1
  15. package/dist/services/base/CommonlibMessages.d.ts +318 -0
  16. package/dist/services/base/CommonlibMessages.js +313 -0
  17. package/dist/services/base/MessageTranslator.d.ts +5 -2
  18. package/dist/services/base/MessageTranslator.js +21 -0
  19. package/dist/services/base/ServiceBase.d.ts +1 -1
  20. package/dist/services/base/ServiceBase.js +2 -4
  21. package/dist/services/base/SettingService.d.ts +3 -0
  22. package/dist/services/base/SettingService.js +3 -2
  23. package/docs/development.md +27 -14
  24. package/docs/p2p-transport-lifecycle.md +10 -2
  25. package/docs/proven-in-use.md +18 -12
  26. package/docs/releasing.md +14 -0
  27. package/package.json +1 -26
  28. package/dist/common/i18n.d.ts +0 -18
  29. package/dist/common/i18n.js +0 -101
  30. package/dist/common/messages/combinedMessages.dev.d.ts +0 -7
  31. package/dist/common/messages/combinedMessages.dev.js +0 -44
  32. package/dist/common/messages/combinedMessages.prod.d.ts +0 -9276
  33. package/dist/common/messages/combinedMessages.prod.js +0 -9283
  34. package/dist/common/messages/de.d.ts +0 -297
  35. package/dist/common/messages/de.js +0 -7
  36. package/dist/common/messages/def.d.ts +0 -1123
  37. package/dist/common/messages/def.js +0 -7
  38. package/dist/common/messages/es.d.ts +0 -689
  39. package/dist/common/messages/es.js +0 -7
  40. package/dist/common/messages/fr.d.ts +0 -582
  41. package/dist/common/messages/fr.js +0 -7
  42. package/dist/common/messages/he.d.ts +0 -583
  43. package/dist/common/messages/he.js +0 -7
  44. package/dist/common/messages/ja.d.ts +0 -838
  45. package/dist/common/messages/ja.js +0 -7
  46. package/dist/common/messages/ko.d.ts +0 -800
  47. package/dist/common/messages/ko.js +0 -7
  48. package/dist/common/messages/ru.d.ts +0 -858
  49. package/dist/common/messages/ru.js +0 -7
  50. package/dist/common/messages/zh-tw.d.ts +0 -384
  51. package/dist/common/messages/zh-tw.js +0 -7
  52. package/dist/common/messages/zh.d.ts +0 -1093
  53. package/dist/common/messages/zh.js +0 -7
  54. package/dist/common/messagesJson/de.json +0 -295
  55. package/dist/common/messagesJson/en.json +0 -1121
  56. package/dist/common/messagesJson/es.json +0 -687
  57. package/dist/common/messagesJson/fr.json +0 -580
  58. package/dist/common/messagesJson/he.json +0 -581
  59. package/dist/common/messagesJson/ja.json +0 -836
  60. package/dist/common/messagesJson/ko.json +0 -798
  61. package/dist/common/messagesJson/ru.json +0 -856
  62. package/dist/common/messagesJson/zh-tw.json +0 -382
  63. package/dist/common/messagesJson/zh.json +0 -1091
  64. package/dist/common/rosetta.d.ts +0 -42
  65. package/dist/common/rosetta.js +0 -57
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Self-hosted LiveSync Commonlib
2
2
 
3
- Commonlib is the ESM package which provides shared data, storage, replication, host-context, and RPC primitives for 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
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
6
 
@@ -24,8 +24,8 @@ The package is ESM-only and declares Node 20 or later. Browser entry points are
24
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
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
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 | Focused platform entry; some convenience exports still require 1.0 classification |
28
- | `@vrtmrz/livesync-commonlib/rpc` | Transport-neutral RPC rooms, sessions, errors, and the PouchDB RPC bridge | Deliberate export, but its 1.0 stability status is not yet accepted |
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
29
  | `@vrtmrz/livesync-commonlib/remote-configurations` | Multiple-remote profile creation, naming, and selection | Focused, package-tested pre-1.0 contract |
30
30
  | `@vrtmrz/livesync-commonlib/settings` | New-Vault defaults, stored-setting fallbacks, and settings migration results | Focused, package-tested pre-1.0 contract |
31
31
  | `@vrtmrz/livesync-commonlib/compat/*` | Exact legacy imports still required by existing clients | Migration-only; paths may be removed as consumers migrate |
@@ -33,13 +33,13 @@ The package is ESM-only and declares Node 20 or later. Browser entry points are
33
33
 
34
34
  Import only the entry point required by the current runtime. Browser code must not import the Node entry.
35
35
 
36
- TODO: decide whether the Node entry's `fs`, `fsPromises`, `path`, `os`, `readline`, URL, and built-in-module convenience exports belong in the 1.0 public contract or should move behind compatibility paths.
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
37
 
38
- TODO: decide whether the draft RPC protocol and PouchDB bridge are stable 1.0 contracts, remain explicitly experimental, or move to a separately versioned package. Until that decision, pin the exact package version when consuming the RPC entry.
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
39
 
40
40
  ## Host context and initialisation
41
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 uses a translator which returns message keys unchanged:
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
43
 
44
44
  ```ts
45
45
  import {
@@ -61,7 +61,9 @@ The host may instead extend `ServiceContext` with platform-specific capabilities
61
61
 
62
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
63
 
64
- TODO: define a reviewed high-level composition factory and its initialisation, readiness, error, and disposal contract. Until that exists, service implementations exposed under `compat/*` retain their owning client's lifecycle rather than becoming a general public composition API.
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.
65
67
 
66
68
  ## Rooted storage
67
69
 
@@ -114,9 +116,9 @@ See [the remote configuration profile guide](docs/remote-configurations.md) for
114
116
 
115
117
  The package is currently an infrastructure and compatibility boundary. The context, rooted-storage, and standard-I/O result contracts have focused cross-platform or instance-isolation tests. Platform details which cannot be shared, such as file timestamp fidelity and browser permission handling, remain host concerns and are documented separately.
116
118
 
117
- `DirectFileManipulator` is useful for existing integrations, but its enumeration, watch ownership, failure, conflict, concurrency, readiness, and disposal semantics are not yet a stable high-level SDK contract.
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.
118
120
 
119
- TODO: define and document the first high-level client façade before declaring list, get, put, delete, watch, and close behaviour stable.
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.
120
122
 
121
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), and [the remote configuration profile guide](docs/remote-configurations.md).
122
124
 
@@ -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";
@@ -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
  }
@@ -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
  */
@@ -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;
@@ -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;
@@ -1,4 +1,4 @@
1
- import { $t } from "./i18n.js";
1
+ import { englishMessageTranslator } from "../services/base/MessageTranslator.js";
2
2
  import {
3
3
  DEFAULT_SETTINGS,
4
4
  configurationNames
@@ -399,7 +399,7 @@ const SettingInformation = {
399
399
  desc: "Files with modification times greater than this value (in seconds since the Unix epoch) will not have their events reflected. Set to 0 to disable this limit."
400
400
  }
401
401
  };
402
- function translateInfo(infoSrc, translate = (message) => $t(message)) {
402
+ function translateInfo(infoSrc, translate = englishMessageTranslator) {
403
403
  if (!infoSrc) return false;
404
404
  const info = { ...infoSrc };
405
405
  info.name = translate(info.name);
package/dist/context.d.ts CHANGED
@@ -5,5 +5,6 @@
5
5
  */
6
6
  export { ServiceContext, createServiceContext, type ServiceContextContract, type ServiceContextOptions, } from "./services/base/ServiceBase.js";
7
7
  export { createLiveSyncEventHub, type LiveSyncEventHub } from "./hub/hub.js";
8
- export { passthroughMessageTranslator, type MessageTranslator, type TranslationParameters, } from "./services/base/MessageTranslator.js";
8
+ export { englishMessageTranslator, passthroughMessageTranslator, type MessageTranslator, type TranslationParameters, } from "./services/base/MessageTranslator.js";
9
+ export { commonlibEnglishMessages, type CommonlibMessageKey } from "./services/base/CommonlibMessages.js";
9
10
  export type { StandardIo, StandardIoChunk } from "./platform/standardIo.js";
package/dist/context.js CHANGED
@@ -4,11 +4,15 @@ import {
4
4
  } from "./services/base/ServiceBase.js";
5
5
  import { createLiveSyncEventHub } from "./hub/hub.js";
6
6
  import {
7
+ englishMessageTranslator,
7
8
  passthroughMessageTranslator
8
9
  } from "./services/base/MessageTranslator.js";
10
+ import { commonlibEnglishMessages } from "./services/base/CommonlibMessages.js";
9
11
  export {
10
12
  ServiceContext,
13
+ commonlibEnglishMessages,
11
14
  createLiveSyncEventHub,
12
15
  createServiceContext,
16
+ englishMessageTranslator,
13
17
  passthroughMessageTranslator
14
18
  };
@@ -1,6 +1,7 @@
1
1
  import { type EntryDoc, type DatabaseConnectingStatus, type RemoteDBSettings, type EntryLeaf, type TweakValues, type NodeData } from "../common/types.js";
2
2
  import type { RequiredServices } from "../interfaces/ServiceModule.js";
3
3
  import type { TranslationParameters } from "../services/base/MessageTranslator.js";
4
+ import type { CommonlibMessageKey } from "../services/base/CommonlibMessages.js";
4
5
  export type ReplicationCallback = (e: PouchDB.Core.ExistingDocument<EntryDoc>[]) => Promise<boolean> | boolean;
5
6
  export type ReplicationStat = {
6
7
  sent: number;
@@ -37,7 +38,7 @@ export declare abstract class LiveSyncAbstractReplicator {
37
38
  get database(): import("../pouchdb/LiveSyncLocalDB.js").LiveSyncLocalDB;
38
39
  get rawDatabase(): PouchDB.Database<EntryDoc>;
39
40
  get currentSettings(): import("../common/types.js").ObsidianLiveSyncSettings;
40
- protected translate<TKey extends string>(key: TKey, params?: TranslationParameters): string;
41
+ protected translate(key: CommonlibMessageKey, params?: TranslationParameters): string;
41
42
  sendChunks(setting: RemoteDBSettings, remoteDB: PouchDB.Database<EntryDoc> | undefined, showResult: boolean, fromSeq?: number | string): Promise<boolean>;
42
43
  abstract getReplicationPBKDF2Salt(setting: RemoteDBSettings, refresh?: boolean): Promise<Uint8Array<ArrayBuffer>>;
43
44
  ensurePBKDF2Salt(setting: RemoteDBSettings, showMessage?: boolean, useCache?: boolean): Promise<boolean>;
@@ -1,5 +1,5 @@
1
1
  import { FlagFilesHumanReadable } from "../common/models/redflag.const.js";
2
- import { REMOTE_COUCHDB, REMOTE_MINIO } from "../common/models/setting.const.js";
2
+ import { REMOTE_COUCHDB, REMOTE_MINIO, REMOTE_P2P } from "../common/models/setting.const.js";
3
3
  import { DEFAULT_SETTINGS } from "../common/models/setting.const.defaults.js";
4
4
  import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
5
5
  import { delay } from "octagonal-wheels/promises";
@@ -108,6 +108,9 @@ Please enable them from the settings screen after setup is complete.`,
108
108
  await this.resetLocalDatabase();
109
109
  await delay(1e3);
110
110
  await this.databaseEvents.initialiseDatabase(true, true, true);
111
+ if (this.setting.currentSettings().remoteType === REMOTE_P2P) {
112
+ return;
113
+ }
111
114
  await this.replication.markLocked();
112
115
  await this._tryResetRemoteDatabase();
113
116
  await this.replication.markLocked();
@@ -273,8 +276,10 @@ Are you sure you wish to proceed?`;
273
276
  await this.replication.markResolved();
274
277
  await delay(500);
275
278
  await this.replication.replicateAllFromRemote(true);
276
- await delay(1e3);
277
- await this.replication.replicateAllFromRemote(true);
279
+ if (this.setting.currentSettings().remoteType !== REMOTE_P2P) {
280
+ await delay(1e3);
281
+ await this.replication.replicateAllFromRemote(true);
282
+ }
278
283
  if (autoResume) {
279
284
  await this.finishRebuild();
280
285
  }
@@ -8,6 +8,7 @@ import type { ReplicatorService } from "./base/ReplicatorService.js";
8
8
  import type { UIService } from "./implements/base/UIService.js";
9
9
  import type { InjectableAPIService } from "./implements/injectable/InjectableAPIService.js";
10
10
  import type { KeyValueDatabaseFactory } from "../interfaces/KeyValueDatabase.js";
11
+ import type { ObsidianLiveSyncSettings } from "../common/types.js";
11
12
  export type BrowserServiceHostDependencies<T extends ServiceContext> = {
12
13
  API: InjectableAPIService<T>;
13
14
  appLifecycle: AppLifecycleService<T>;
@@ -29,6 +30,7 @@ export type BrowserServiceHubOptions<T extends ServiceContext> = {
29
30
  context?: T;
30
31
  host: BrowserServiceHost<T>;
31
32
  openKeyValueDatabase?: KeyValueDatabaseFactory;
33
+ onDisplayLanguageChanged?: (language: ObsidianLiveSyncSettings["displayLanguage"]) => void;
32
34
  };
33
35
  export declare class BrowserServiceHub<T extends ServiceContext> extends InjectableServiceHub<T> {
34
36
  get vault(): InjectableVaultServiceCompat<T>;
@@ -32,7 +32,10 @@ class BrowserServiceHub extends InjectableServiceHub {
32
32
  const API = options.host.createAPI(context);
33
33
  const conflict = new InjectableConflictService(context);
34
34
  const fileProcessing = new InjectableFileProcessingService(context);
35
- const setting = new InjectableSettingService(context, { APIService: API });
35
+ const setting = new InjectableSettingService(context, {
36
+ APIService: API,
37
+ onDisplayLanguageChanged: options.onDisplayLanguageChanged
38
+ });
36
39
  const appLifecycle = new BrowserAppLifecycleService(context, { settingService: setting });
37
40
  const remote = new InjectableRemoteService(context, {
38
41
  pouchDB: PouchDB,
@@ -4,11 +4,13 @@ import type { DatabaseService } from "./base/DatabaseService.js";
4
4
  import type { Constructor } from "../common/utils.type.js";
5
5
  import type { KeyValueDatabaseFactory } from "../interfaces/KeyValueDatabase.js";
6
6
  import type { PouchDBConstructor } from "../pouchdb/PouchDBConstructor.js";
7
+ import type { ObsidianLiveSyncSettings } from "../common/types.js";
7
8
  export declare class HeadlessServiceHub<T extends ServiceContext> extends InjectableServiceHub<T> {
8
9
  constructor(_context: T | undefined, overrideServiceConstructor: {
9
10
  /** PouchDB with the adapters required by this headless runtime. */
10
11
  pouchDB: PouchDBConstructor;
11
12
  database?: Constructor<DatabaseService<T>>;
12
13
  openKeyValueDatabase?: KeyValueDatabaseFactory;
14
+ onDisplayLanguageChanged?: (language: ObsidianLiveSyncSettings["displayLanguage"]) => void;
13
15
  });
14
16
  }
@@ -54,7 +54,8 @@ class HeadlessServiceHub extends InjectableServiceHub {
54
54
  const conflict = new InjectableConflictService(context);
55
55
  const fileProcessing = new InjectableFileProcessingService(context);
56
56
  const setting = new InjectableSettingService(context, {
57
- APIService: API
57
+ APIService: API,
58
+ onDisplayLanguageChanged: overrideServiceConstructor.onDisplayLanguageChanged
58
59
  });
59
60
  const appLifecycle = new HeadlessAppLifecycleService(context, {
60
61
  settingService: setting