@vrtmrz/livesync-commonlib 0.1.44 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -4
- package/dist/API/DirectFileManipulatorV2.d.ts +2 -0
- package/dist/API/DirectFileManipulatorV2.js +3 -0
- package/dist/API/processSetting.d.ts +1 -0
- package/dist/common/configForDoc.d.ts +1 -0
- package/dist/common/configForDoc.js +11 -1
- package/dist/common/models/db.definition.d.ts +1 -0
- package/dist/common/models/setting.const.defaults.js +1 -0
- package/dist/common/models/setting.const.preferred.js +1 -3
- package/dist/common/models/setting.const.qr.js +1 -0
- package/dist/common/models/setting.policy.d.ts +4 -0
- package/dist/common/models/setting.policy.js +1 -0
- package/dist/common/models/setting.type.d.ts +2 -0
- package/dist/common/models/shared.definition.d.ts +20 -0
- package/dist/common/models/tweak.definition.d.ts +1 -0
- package/dist/common/settingConstants.d.ts +1 -0
- package/dist/common/settingConstants.js +6 -2
- package/dist/interfaces/FileHandler.d.ts +5 -1
- package/dist/interfaces/StorageEventManager.d.ts +5 -0
- package/dist/interfaces/StorageEventManager.js +7 -0
- package/dist/managers/EntryManager/EntryManagerImpls.js +3 -2
- package/dist/managers/StorageEventManager.d.ts +19 -10
- package/dist/managers/StorageEventManager.js +259 -189
- package/dist/pouchdb/LiveSyncDBFunctions.js +4 -0
- package/dist/pouchdb/StreamingFetch.d.ts +5 -1
- package/dist/pouchdb/StreamingFetch.js +529 -65
- package/dist/replication/LiveSyncAbstractReplicator.d.ts +2 -0
- package/dist/replication/LiveSyncAbstractReplicator.js +5 -2
- package/dist/replication/RemoteResource.d.ts +23 -2
- package/dist/replication/RemoteResource.js +6 -2
- package/dist/replication/couchdb/LiveSyncReplicator.d.ts +14 -0
- package/dist/replication/couchdb/LiveSyncReplicator.js +171 -3
- package/dist/replication/couchdb/fastForward.d.ts +19 -0
- package/dist/replication/couchdb/fastForward.js +119 -0
- package/dist/replication/couchdb/replicationProgress.d.ts +6 -0
- package/dist/replication/couchdb/replicationProgress.js +105 -0
- package/dist/replication/index.d.ts +4 -1
- package/dist/replication/joinedDevices.d.ts +25 -0
- package/dist/replication/joinedDevices.js +123 -0
- package/dist/replication/journal/JournalSyncCore.d.ts +2 -1
- package/dist/replication/journal/JournalSyncCore.js +11 -1
- package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +4 -0
- package/dist/replication/journal/LiveSyncJournalReplicator.js +56 -1
- package/dist/replication/journal/objectstore/JournalStorageAdapter.d.ts +3 -0
- package/dist/replication/journal/objectstore/MinioStorageAdapter.d.ts +2 -1
- package/dist/replication/journal/objectstore/MinioStorageAdapter.js +27 -1
- package/dist/replication/trystero/P2PRoomSessionOwner.d.ts +1 -1
- package/dist/replication/trystero/P2PRoomSessionOwner.js +27 -14
- package/dist/replication/trystero/TrysteroReplicator.d.ts +2 -0
- package/dist/serviceFeatures/offlineScanner.js +19 -7
- package/dist/serviceModules/Rebuilder.js +41 -15
- package/dist/serviceModules/ServiceFileAccessBase.js +5 -0
- package/dist/serviceModules/ServiceFileHandlerBase.d.ts +8 -5
- package/dist/serviceModules/ServiceFileHandlerBase.js +75 -38
- package/dist/services/BrowserServices.d.ts +4 -2
- package/dist/services/BrowserServices.js +2 -5
- package/dist/services/HeadlessServices.d.ts +3 -0
- package/dist/services/HeadlessServices.js +2 -5
- package/dist/services/ServiceHub.d.ts +4 -4
- package/dist/services/base/AppLifecycleService.js +1 -1
- package/dist/services/base/ControlService.js +6 -2
- package/dist/services/base/FileProcessingService.d.ts +7 -1
- package/dist/services/base/FileProcessingService.js +9 -1
- package/dist/services/base/IService.d.ts +19 -19
- package/dist/services/base/ReplicatorService.d.ts +1 -0
- package/dist/services/base/ReplicatorService.remoteResourceResolver.js +1 -1
- package/dist/services/base/SettingService.d.ts +8 -18
- package/dist/services/base/SettingService.js +28 -46
- package/dist/services/implements/base/SvelteDialog.d.ts +2 -2
- package/dist/services/implements/base/UIService.d.ts +2 -0
- package/dist/services/implements/base/UIService.js +5 -0
- package/dist/services/implements/injectable/InjectableServiceHub.d.ts +4 -4
- package/dist/services/implements/injectable/InjectableServices.d.ts +2 -2
- package/dist/services/implements/injectable/InjectableSettingService.d.ts +0 -3
- package/dist/services/implements/injectable/InjectableSettingService.js +0 -11
- package/dist/services/lib/HandlerUtils.d.ts +60 -0
- package/dist/services/lib/HandlerUtils.js +46 -0
- package/dist/string_and_binary/chunks.js +7 -5
- package/dist/uiCommands.d.ts +26 -0
- package/dist/uiCommands.js +57 -0
- package/dist/worker/bgWorker.js +1 -1
- package/docs/development.md +15 -3
- package/docs/offline-scanner.md +15 -13
- package/docs/proven-in-use.md +38 -4
- package/docs/releasing.md +20 -13
- package/docs/settings-lifecycle.md +39 -5
- package/docs/storage-events-and-reflection.md +109 -10
- package/package.json +13 -18
- package/dist/services/base/ConfigService.d.ts +0 -7
- package/dist/services/base/ConfigService.js +0 -6
- package/dist/services/implements/browser/ConfigServiceBrowserCompat.d.ts +0 -16
- package/dist/services/implements/browser/ConfigServiceBrowserCompat.js +0 -23
|
@@ -40,7 +40,9 @@ class ControlService extends ServiceBase {
|
|
|
40
40
|
await this.services.appLifecycleService.onSuspending();
|
|
41
41
|
await this.services.settingService.onBeforeRealiseSetting();
|
|
42
42
|
this.services.databaseService.localDatabase.refreshSettings();
|
|
43
|
-
await this.services.fileProcessingService.commitPendingFileEvents()
|
|
43
|
+
if (!await this.services.fileProcessingService.commitPendingFileEvents()) {
|
|
44
|
+
throw new Error("Pending local file operations could not be completed before applying settings");
|
|
45
|
+
}
|
|
44
46
|
await this.services.settingService.onRealiseSetting();
|
|
45
47
|
if (this.services.appLifecycleService.isSuspended()) return;
|
|
46
48
|
await this.services.appLifecycleService.onResuming();
|
|
@@ -49,7 +51,8 @@ class ControlService extends ServiceBase {
|
|
|
49
51
|
}
|
|
50
52
|
async _onLiveSyncUnload() {
|
|
51
53
|
this.context.events.emitEvent(EVENT_PLUGIN_UNLOADED);
|
|
52
|
-
await this.services.appLifecycleService.onBeforeUnload();
|
|
54
|
+
const prepared = await this.services.appLifecycleService.onBeforeUnload();
|
|
55
|
+
if (!prepared) this._log("Unload preparation failed; pending storage work may require recovery", LOG_LEVEL_URGENT);
|
|
53
56
|
await Promise.resolve(this.services.appLifecycleService.onAppUnload());
|
|
54
57
|
cancelAllPeriodicTask();
|
|
55
58
|
cancelAllTasks();
|
|
@@ -64,6 +67,7 @@ class ControlService extends ServiceBase {
|
|
|
64
67
|
this.context.events.emitEvent(EVENT_PLATFORM_UNLOADED);
|
|
65
68
|
this.context.events.offAll();
|
|
66
69
|
this._log(this.context.translate("moduleLiveSyncMain.logUnloadingPlugin"));
|
|
70
|
+
if (!prepared) throw new Error("Unload preparation failed");
|
|
67
71
|
return;
|
|
68
72
|
}
|
|
69
73
|
/**
|
|
@@ -9,10 +9,16 @@ export declare class FileProcessingService<T extends ServiceContext = ServiceCon
|
|
|
9
9
|
*/
|
|
10
10
|
readonly processFileEvent: import("../lib/HandlerUtils.js").BooleanMultipleHandlerFunction<(item: import("../../common/types.js").FileEventItem) => Promise<boolean>>;
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Dispatch a legacy optional notification, without confirming persistence.
|
|
13
13
|
* i.e., hidden files synchronisation or customisation sync.
|
|
14
14
|
*/
|
|
15
15
|
readonly processOptionalFileEvent: import("../lib/HandlerUtils.js").BooleanMultipleHandlerFunction<(path: import("../../common/types.js").FilePath) => Promise<boolean>>;
|
|
16
|
+
/**
|
|
17
|
+
* Complete optional file operations through the first applicable handler.
|
|
18
|
+
* Hosts migrate all optional providers together. Once any typed provider is
|
|
19
|
+
* registered, dispatch uses this contract exclusively.
|
|
20
|
+
*/
|
|
21
|
+
readonly processOptionalFileEventWithResult?: import("../lib/HandlerUtils.js").FirstHandledFunction<(path: import("../../common/types.js").FilePath, options?: import("./IService.js").OptionalFileEventOptions) => Promise<import("../lib/HandlerUtils.js").HandlerOutcome<void, string>>>;
|
|
16
22
|
/**
|
|
17
23
|
* Commit any pending file events that have been queued for processing.
|
|
18
24
|
*/
|
|
@@ -7,10 +7,18 @@ class FileProcessingService extends ServiceBase {
|
|
|
7
7
|
*/
|
|
8
8
|
processFileEvent = handlers().anySuccess("processFileEvent");
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Dispatch a legacy optional notification, without confirming persistence.
|
|
11
11
|
* i.e., hidden files synchronisation or customisation sync.
|
|
12
12
|
*/
|
|
13
13
|
processOptionalFileEvent = handlers().anySuccess("processOptionalFileEvent");
|
|
14
|
+
/**
|
|
15
|
+
* Complete optional file operations through the first applicable handler.
|
|
16
|
+
* Hosts migrate all optional providers together. Once any typed provider is
|
|
17
|
+
* registered, dispatch uses this contract exclusively.
|
|
18
|
+
*/
|
|
19
|
+
processOptionalFileEventWithResult = handlers().firstHandled(
|
|
20
|
+
"processOptionalFileEventWithResult"
|
|
21
|
+
);
|
|
14
22
|
/**
|
|
15
23
|
* Commit any pending file events that have been queued for processing.
|
|
16
24
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ValueStore, ValueDeleter } from "@vrtmrz/value-store";
|
|
1
2
|
import type { FetchHttpHandler } from "@smithy/fetch-http-handler";
|
|
2
3
|
import { type LOG_LEVEL } from "octagonal-wheels/common/logger";
|
|
3
4
|
import type { AnyEntry, AUTO_MERGED, CouchDBCredentials, ConfigPassphraseStore, diff_result, DocumentID, EntryDoc, EntryHasPath, FileEventItem, FilePath, FilePathWithPrefix, LoadedEntry, MetaEntry, MISSING_OR_ERROR, ObsidianLiveSyncSettings, RemoteDBSettings, RemotePreferredTweakResult, SettingsMigrationState, TweakAssessment, TweakValues, UXFileInfo, UXFileInfoStub } from "../../common/types.js";
|
|
@@ -15,8 +16,9 @@ import type { OwnedCouchDBConnection, RemoteConnectionOpenOptions } from "./Remo
|
|
|
15
16
|
import type { ActiveReplicatorContext, ContinuousReplicationRequest, ReplicationAttemptFailure, ReplicationFailureRequest, ReplicatorProviderDefinitionMap, ReplicationOutcome, ReplicationReadinessRequirements, UnattendedOneShotRequest, UserInitiatedOneShotRequest } from "../../replication/ReplicatorProvider.js";
|
|
16
17
|
import type { RemoteResourceKind, RemoteResourceMap } from "../../replication/RemoteResource.js";
|
|
17
18
|
import type { CentralRemoteAdministrationRequest, CentralRemoteAdministrationResult } from "../../replication/CentralRemoteAdministration.js";
|
|
18
|
-
import type { MultipleHandlerFunction } from "../lib/HandlerUtils.js";
|
|
19
|
+
import type { HandlerOutcome, MultipleHandlerFunction } from "../lib/HandlerUtils.js";
|
|
19
20
|
import type { VaultScanResult } from "./VaultScanResult.js";
|
|
21
|
+
import type { UICommandRegistry } from "../../uiCommands.js";
|
|
20
22
|
declare global {
|
|
21
23
|
interface OPTIONAL_SYNC_FEATURES {
|
|
22
24
|
DISABLE: "DISABLE";
|
|
@@ -95,9 +97,17 @@ export interface IKeyValueDBService {
|
|
|
95
97
|
openSimpleStore<T>(kind: string): SimpleStore<T>;
|
|
96
98
|
simpleStore: SimpleStore<unknown>;
|
|
97
99
|
}
|
|
100
|
+
/** Options for completing an admitted optional file event. */
|
|
101
|
+
export interface OptionalFileEventOptions {
|
|
102
|
+
/** Resolves when a finite flush needs this event to finish without its debounce delay. */
|
|
103
|
+
whenFlushRequested?: Promise<void>;
|
|
104
|
+
}
|
|
98
105
|
export interface IFileProcessingService {
|
|
99
106
|
processFileEvent(item: FileEventItem): Promise<boolean>;
|
|
107
|
+
/** Legacy notification contract; its boolean does not confirm persistence. */
|
|
100
108
|
processOptionalFileEvent(path: FilePath): Promise<boolean>;
|
|
109
|
+
/** Complete the actual optional file operation, or explain why it is not completed. */
|
|
110
|
+
processOptionalFileEventWithResult?(path: FilePath, options?: OptionalFileEventOptions): Promise<HandlerOutcome<void, string>>;
|
|
101
111
|
commitPendingFileEvents(): Promise<boolean>;
|
|
102
112
|
batched: ReactiveSource<number>;
|
|
103
113
|
processing: ReactiveSource<number>;
|
|
@@ -312,16 +322,11 @@ export interface ISettingService {
|
|
|
312
322
|
getSettingsMigrationState(): SettingsMigrationState | undefined;
|
|
313
323
|
getDeviceAndVaultName(): string;
|
|
314
324
|
setDeviceAndVaultName(name: string): void;
|
|
315
|
-
saveDeviceAndVaultName(): void
|
|
316
|
-
/**
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
getDeviceLocalConfig(key: string): string | null;
|
|
321
|
-
/** Store host-provided, device-local configuration by its exact key. */
|
|
322
|
-
setDeviceLocalConfig(key: string, value: string): void;
|
|
323
|
-
/** Delete host-provided, device-local configuration by its exact key. */
|
|
324
|
-
deleteDeviceLocalConfig(key: string): void;
|
|
325
|
+
saveDeviceAndVaultName(): Promise<void>;
|
|
326
|
+
/** Exact-key host storage; values are local to this application instance. */
|
|
327
|
+
readonly deviceValues: ValueStore & ValueDeleter;
|
|
328
|
+
/** Storage scoped by the current Vault and database suffix. */
|
|
329
|
+
readonly config: IConfigService;
|
|
325
330
|
onBeforeSaveSettingData(nextSettings: ObsidianLiveSyncSettings, previousSettings: ObsidianLiveSyncSettings): Promise<(Partial<ObsidianLiveSyncSettings> | void)[]>;
|
|
326
331
|
saveSettingData(): Promise<void>;
|
|
327
332
|
/** Save a new configuration wrapper and its device-local key, retaining the protected values. */
|
|
@@ -333,9 +338,6 @@ export interface ISettingService {
|
|
|
333
338
|
onSettingLoaded(settings: ObsidianLiveSyncSettings): Promise<boolean>;
|
|
334
339
|
onSettingChanged(settings: ObsidianLiveSyncSettings): Promise<boolean>;
|
|
335
340
|
onSettingSaved(settings: ObsidianLiveSyncSettings): Promise<boolean>;
|
|
336
|
-
getSmallConfig(key: string): string | null;
|
|
337
|
-
setSmallConfig(key: string, value: string): void;
|
|
338
|
-
deleteSmallConfig(key: string): void;
|
|
339
341
|
}
|
|
340
342
|
export interface ITweakValueService {
|
|
341
343
|
fetchRemotePreferred(trialSetting: RemoteDBSettings): Promise<RemotePreferredTweakResult>;
|
|
@@ -373,15 +375,13 @@ export interface ITestService {
|
|
|
373
375
|
addTestResult(name: string, key: string, result: boolean, summary?: string, message?: string): void;
|
|
374
376
|
}
|
|
375
377
|
export interface IUIService {
|
|
378
|
+
readonly commands: UICommandRegistry;
|
|
376
379
|
promptCopyToClipboard(title: string, value: string): Promise<boolean>;
|
|
377
380
|
showMarkdownDialog<T extends string[]>(title: string, contentMD: string, buttons: T): Promise<(typeof buttons)[number] | false>;
|
|
378
381
|
get confirm(): Confirm;
|
|
379
382
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
setSmallConfig(key: string, value: string): void;
|
|
383
|
-
deleteSmallConfig(key: string): void;
|
|
384
|
-
}
|
|
383
|
+
/** Asynchronous device configuration selected by the host composition. */
|
|
384
|
+
export type IConfigService = ValueStore & ValueDeleter;
|
|
385
385
|
export interface IServiceHub {
|
|
386
386
|
API: IAPIService;
|
|
387
387
|
path: IPathService;
|
|
@@ -125,5 +125,6 @@ export declare abstract class ReplicatorService<T extends ServiceContext = Servi
|
|
|
125
125
|
lastSyncPullSeq: number;
|
|
126
126
|
lastSyncPushSeq: number;
|
|
127
127
|
syncStatus: import("../../common/types.js").DatabaseConnectingStatus;
|
|
128
|
+
couchDB?: import("../../common/models/shared.definition.js").CouchDBReplicationProgress;
|
|
128
129
|
}>;
|
|
129
130
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CAPABILITY_SUPPORT_KINDS } from "../../replication/ProviderCapability.js";
|
|
2
2
|
async function resolveRemoteResource(capabilities, kind, setting) {
|
|
3
3
|
const capability = capabilities[kind];
|
|
4
|
-
if (capability
|
|
4
|
+
if (capability?.kind !== CAPABILITY_SUPPORT_KINDS.SUPPORTED) {
|
|
5
5
|
return void 0;
|
|
6
6
|
}
|
|
7
7
|
return await capability.run(setting);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ValueStore, type ValueDeleter } from "@vrtmrz/value-store";
|
|
1
2
|
import { type ConfigPassphraseStore, type ObsidianLiveSyncSettings, type SettingsMigrationState } from "../../common/types.js";
|
|
2
3
|
import type { IAPIService, ISettingService } from "./IService.js";
|
|
3
4
|
import { ServiceBase, type ServiceContext } from "./ServiceBase.js";
|
|
@@ -5,6 +6,8 @@ import { createInstanceLogFunction } from "../lib/logUtils.js";
|
|
|
5
6
|
import { type PersistedSettings } from "../../common/models/setting.policy.js";
|
|
6
7
|
export interface SettingServiceDependencies {
|
|
7
8
|
APIService: IAPIService;
|
|
9
|
+
/** Host-owned exact-key storage, created once for this application instance. */
|
|
10
|
+
deviceValues: ValueStore & ValueDeleter;
|
|
8
11
|
/** Optional host hook for applying the loaded display language to its catalogue. */
|
|
9
12
|
onDisplayLanguageChanged?: (language: ObsidianLiveSyncSettings["displayLanguage"]) => void;
|
|
10
13
|
}
|
|
@@ -12,9 +15,10 @@ export declare abstract class SettingService<T extends ServiceContext = ServiceC
|
|
|
12
15
|
deviceAndVaultName: string;
|
|
13
16
|
protected APIService: IAPIService;
|
|
14
17
|
private readonly onDisplayLanguageChanged?;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
/** Exact-key device values, including legacy keys outside the Vault namespace. */
|
|
19
|
+
readonly deviceValues: ValueStore & ValueDeleter;
|
|
20
|
+
/** Device values scoped by the current Vault name and database suffix. */
|
|
21
|
+
readonly config: ValueStore & ValueDeleter;
|
|
18
22
|
_settings: ObsidianLiveSyncSettings;
|
|
19
23
|
get settings(): ObsidianLiveSyncSettings;
|
|
20
24
|
set settings(value: ObsidianLiveSyncSettings);
|
|
@@ -43,23 +47,9 @@ export declare abstract class SettingService<T extends ServiceContext = ServiceC
|
|
|
43
47
|
/**
|
|
44
48
|
* Save the current device and vault name to settings, aside from the main settings.
|
|
45
49
|
*/
|
|
46
|
-
saveDeviceAndVaultName(): void
|
|
47
|
-
/**
|
|
48
|
-
* Read an exact key from the host's device-local configuration store.
|
|
49
|
-
*
|
|
50
|
-
* Unlike the main settings document, this state is not synchronised. Callers
|
|
51
|
-
* which need Vault namespacing should normally use {@link getSmallConfig}.
|
|
52
|
-
*/
|
|
53
|
-
getDeviceLocalConfig(key: string): string | null;
|
|
54
|
-
/** Store an exact key in the host's non-synchronised configuration store. */
|
|
55
|
-
setDeviceLocalConfig(key: string, value: string): void;
|
|
56
|
-
/** Delete an exact key from the host's non-synchronised configuration store. */
|
|
57
|
-
deleteDeviceLocalConfig(key: string): void;
|
|
50
|
+
saveDeviceAndVaultName(): Promise<void>;
|
|
58
51
|
private additionalSuffixOfDatabaseName;
|
|
59
52
|
private getKey;
|
|
60
|
-
setSmallConfig(key: string, value: string): void;
|
|
61
|
-
getSmallConfig(key: string): string;
|
|
62
|
-
deleteSmallConfig(key: string): void;
|
|
63
53
|
/**
|
|
64
54
|
* Save the current settings to storage.
|
|
65
55
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createDeletableValueStore } from "@vrtmrz/value-store";
|
|
1
2
|
import {
|
|
2
3
|
ChunkAlgorithmNames,
|
|
3
4
|
LOG_LEVEL_NOTICE,
|
|
@@ -42,6 +43,10 @@ class SettingService extends ServiceBase {
|
|
|
42
43
|
deviceAndVaultName = "";
|
|
43
44
|
APIService;
|
|
44
45
|
onDisplayLanguageChanged;
|
|
46
|
+
/** Exact-key device values, including legacy keys outside the Vault namespace. */
|
|
47
|
+
deviceValues;
|
|
48
|
+
/** Device values scoped by the current Vault name and database suffix. */
|
|
49
|
+
config;
|
|
45
50
|
_settings;
|
|
46
51
|
get settings() {
|
|
47
52
|
return this._settings;
|
|
@@ -59,6 +64,12 @@ class SettingService extends ServiceBase {
|
|
|
59
64
|
constructor(context, dependencies) {
|
|
60
65
|
super(context);
|
|
61
66
|
this.APIService = dependencies.APIService;
|
|
67
|
+
this.deviceValues = dependencies.deviceValues;
|
|
68
|
+
this.config = createDeletableValueStore({
|
|
69
|
+
get: (key) => this.deviceValues.getValue(this.getKey(key)),
|
|
70
|
+
set: (key, value) => this.deviceValues.setValue(this.getKey(key), value),
|
|
71
|
+
delete: (key) => this.deviceValues.delete(this.getKey(key))
|
|
72
|
+
});
|
|
62
73
|
this.onDisplayLanguageChanged = dependencies.onDisplayLanguageChanged;
|
|
63
74
|
this._log = createInstanceLogFunction("SettingService", this.APIService);
|
|
64
75
|
}
|
|
@@ -111,26 +122,9 @@ class SettingService extends ServiceBase {
|
|
|
111
122
|
/**
|
|
112
123
|
* Save the current device and vault name to settings, aside from the main settings.
|
|
113
124
|
*/
|
|
114
|
-
saveDeviceAndVaultName() {
|
|
125
|
+
async saveDeviceAndVaultName() {
|
|
115
126
|
const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.APIService.getSystemVaultName() + this.additionalSuffixOfDatabaseName();
|
|
116
|
-
this.
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Read an exact key from the host's device-local configuration store.
|
|
120
|
-
*
|
|
121
|
-
* Unlike the main settings document, this state is not synchronised. Callers
|
|
122
|
-
* which need Vault namespacing should normally use {@link getSmallConfig}.
|
|
123
|
-
*/
|
|
124
|
-
getDeviceLocalConfig(key) {
|
|
125
|
-
return this.getItem(key);
|
|
126
|
-
}
|
|
127
|
-
/** Store an exact key in the host's non-synchronised configuration store. */
|
|
128
|
-
setDeviceLocalConfig(key, value) {
|
|
129
|
-
this.setItem(key, value);
|
|
130
|
-
}
|
|
131
|
-
/** Delete an exact key from the host's non-synchronised configuration store. */
|
|
132
|
-
deleteDeviceLocalConfig(key) {
|
|
133
|
-
this.deleteItem(key);
|
|
127
|
+
await this.deviceValues.setValue(lsKey, this.deviceAndVaultName);
|
|
134
128
|
}
|
|
135
129
|
additionalSuffixOfDatabaseName() {
|
|
136
130
|
const suffix = this.settings?.additionalSuffixOfDatabaseName;
|
|
@@ -145,18 +139,6 @@ class SettingService extends ServiceBase {
|
|
|
145
139
|
const addSuffix = this.additionalSuffixOfDatabaseName();
|
|
146
140
|
return `${keyMain}${addSuffix}-${key}`;
|
|
147
141
|
}
|
|
148
|
-
setSmallConfig(key, value) {
|
|
149
|
-
const dbKey = this.getKey(key);
|
|
150
|
-
this.setDeviceLocalConfig(dbKey, value);
|
|
151
|
-
}
|
|
152
|
-
getSmallConfig(key) {
|
|
153
|
-
const dbKey = this.getKey(key);
|
|
154
|
-
return this.getDeviceLocalConfig(dbKey) ?? "";
|
|
155
|
-
}
|
|
156
|
-
deleteSmallConfig(key) {
|
|
157
|
-
const dbKey = this.getKey(key);
|
|
158
|
-
this.deleteDeviceLocalConfig(dbKey);
|
|
159
|
-
}
|
|
160
142
|
/**
|
|
161
143
|
* Save the current settings to storage.
|
|
162
144
|
*/
|
|
@@ -178,7 +160,7 @@ class SettingService extends ServiceBase {
|
|
|
178
160
|
if (store !== "" && store !== "LOCALSTORAGE" && store !== "ASK_AT_LAUNCH") {
|
|
179
161
|
throw new Error("The configuration passphrase storage mode is unsupported.");
|
|
180
162
|
}
|
|
181
|
-
const oldLocalKey = this.
|
|
163
|
+
const oldLocalKey = await this.deviceValues.getValue("ls-setting-passphrase");
|
|
182
164
|
const settings = this.cloneSettings(this.settings);
|
|
183
165
|
let targetKey;
|
|
184
166
|
if (store === "LOCALSTORAGE") {
|
|
@@ -196,10 +178,10 @@ class SettingService extends ServiceBase {
|
|
|
196
178
|
const prepared = await this.prepareSettingSave(settings, targetKey);
|
|
197
179
|
const changesLocalKey = store === "LOCALSTORAGE" && targetKey !== oldLocalKey;
|
|
198
180
|
try {
|
|
199
|
-
if (changesLocalKey) this.
|
|
181
|
+
if (changesLocalKey) await this.deviceValues.setValue("ls-setting-passphrase", targetKey);
|
|
200
182
|
await this.writePreparedSettings(prepared);
|
|
201
183
|
} catch (error) {
|
|
202
|
-
if (changesLocalKey) this.restoreConfigurationPassphrase(oldLocalKey, error);
|
|
184
|
+
if (changesLocalKey) await this.restoreConfigurationPassphrase(oldLocalKey, error);
|
|
203
185
|
throw error;
|
|
204
186
|
}
|
|
205
187
|
this.settings.configPassphraseStore = store;
|
|
@@ -210,10 +192,10 @@ class SettingService extends ServiceBase {
|
|
|
210
192
|
throw error;
|
|
211
193
|
});
|
|
212
194
|
}
|
|
213
|
-
restoreConfigurationPassphrase(previous, cause) {
|
|
195
|
+
async restoreConfigurationPassphrase(previous, cause) {
|
|
214
196
|
try {
|
|
215
|
-
if (previous) this.
|
|
216
|
-
else this.
|
|
197
|
+
if (previous) await this.deviceValues.setValue("ls-setting-passphrase", previous);
|
|
198
|
+
else await this.deviceValues.delete("ls-setting-passphrase");
|
|
217
199
|
} catch {
|
|
218
200
|
const message = this.context.translate("SettingService.ConfigurationPassphraseRollbackFailed");
|
|
219
201
|
this._log(message, LOG_LEVEL_URGENT);
|
|
@@ -344,9 +326,9 @@ class SettingService extends ServiceBase {
|
|
|
344
326
|
return { persisted, runtime, patch: savedPatch, passphrase: configurationPassphrase };
|
|
345
327
|
}
|
|
346
328
|
async writePreparedSettings(prepared) {
|
|
347
|
-
this.saveDeviceAndVaultName();
|
|
329
|
+
await this.saveDeviceAndVaultName();
|
|
348
330
|
if (prepared.runtime.P2P_DevicePeerName?.trim()) {
|
|
349
|
-
this.
|
|
331
|
+
await this.config.setValue(SETTING_KEY_P2P_DEVICE_NAME, prepared.runtime.P2P_DevicePeerName.trim());
|
|
350
332
|
}
|
|
351
333
|
await this.saveData(prepared.persisted);
|
|
352
334
|
}
|
|
@@ -528,7 +510,7 @@ class SettingService extends ServiceBase {
|
|
|
528
510
|
getPassphrase(settings) {
|
|
529
511
|
const methods = {
|
|
530
512
|
"": () => Promise.resolve("*"),
|
|
531
|
-
LOCALSTORAGE: () =>
|
|
513
|
+
LOCALSTORAGE: () => this.deviceValues.getValue("ls-setting-passphrase", ""),
|
|
532
514
|
ASK_AT_LAUNCH: () => this.APIService.confirm.askString(
|
|
533
515
|
this.context.translate("SettingService.ConfigurationPassphraseTitle"),
|
|
534
516
|
"passphrase",
|
|
@@ -700,7 +682,7 @@ class SettingService extends ServiceBase {
|
|
|
700
682
|
} else {
|
|
701
683
|
configuredIdKey(settings);
|
|
702
684
|
}
|
|
703
|
-
const previous = this.
|
|
685
|
+
const previous = await this.deviceValues.getValue("ls-setting-passphrase");
|
|
704
686
|
let candidate = previous || false;
|
|
705
687
|
let retry = false;
|
|
706
688
|
while (true) {
|
|
@@ -710,9 +692,9 @@ class SettingService extends ServiceBase {
|
|
|
710
692
|
if (result.verified) {
|
|
711
693
|
if (candidate !== previous) {
|
|
712
694
|
try {
|
|
713
|
-
this.
|
|
695
|
+
await this.deviceValues.setValue("ls-setting-passphrase", candidate);
|
|
714
696
|
} catch (error) {
|
|
715
|
-
this.restoreConfigurationPassphrase(previous, error);
|
|
697
|
+
await this.restoreConfigurationPassphrase(previous, error);
|
|
716
698
|
throw error;
|
|
717
699
|
}
|
|
718
700
|
}
|
|
@@ -788,9 +770,9 @@ class SettingService extends ServiceBase {
|
|
|
788
770
|
}
|
|
789
771
|
const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.APIService.getSystemVaultName() + this.additionalSuffixOfDatabaseName();
|
|
790
772
|
if (this.settings.deviceAndVaultName != "") {
|
|
791
|
-
if (!this.
|
|
773
|
+
if (!await this.deviceValues.getValue(lsKey)) {
|
|
792
774
|
this.setDeviceAndVaultName(this.settings.deviceAndVaultName);
|
|
793
|
-
this.saveDeviceAndVaultName();
|
|
775
|
+
await this.saveDeviceAndVaultName();
|
|
794
776
|
this.settings.deviceAndVaultName = "";
|
|
795
777
|
}
|
|
796
778
|
}
|
|
@@ -801,7 +783,7 @@ class SettingService extends ServiceBase {
|
|
|
801
783
|
);
|
|
802
784
|
this.settings.customChunkSize = 0;
|
|
803
785
|
}
|
|
804
|
-
this.setDeviceAndVaultName(this.
|
|
786
|
+
this.setDeviceAndVaultName(await this.deviceValues.getValue(lsKey) || "");
|
|
805
787
|
if (this.getDeviceAndVaultName() == "") {
|
|
806
788
|
if (this.settings.usePluginSync) {
|
|
807
789
|
this._log("Device name missing. Disabling plug-in sync.", LOG_LEVEL_NOTICE);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IConfigService } from "../../base/IService.js";
|
|
2
2
|
import type { AppLifecycleService } from "../../base/AppLifecycleService.js";
|
|
3
3
|
import type { ReplicatorService } from "../../base/ReplicatorService.js";
|
|
4
4
|
import type { Confirm } from "../../../interfaces/Confirm.js";
|
|
@@ -9,7 +9,7 @@ import type { Constructor } from "../../../common/utils.type.js";
|
|
|
9
9
|
export type SvelteDialogManagerDependencies<T extends ServiceContext = ServiceContext> = {
|
|
10
10
|
appLifecycle: AppLifecycleService<T>;
|
|
11
11
|
replicator: ReplicatorService<T>;
|
|
12
|
-
config:
|
|
12
|
+
config: IConfigService;
|
|
13
13
|
confirm: Confirm;
|
|
14
14
|
control: IControlService;
|
|
15
15
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Confirm } from "../../../interfaces/Confirm.js";
|
|
2
|
+
import { UICommandRegistry } from "../../../uiCommands.js";
|
|
2
3
|
import type { ComponentHasResult, SvelteDialogManager } from "./SvelteDialog.js";
|
|
3
4
|
import type { IAPIService, IUIService } from "../../base/IService.js";
|
|
4
5
|
import { ServiceBase, type ServiceContext } from "../../base/ServiceBase.js";
|
|
@@ -12,6 +13,7 @@ type DialogParams = {
|
|
|
12
13
|
dataToCopy: string;
|
|
13
14
|
};
|
|
14
15
|
export declare abstract class UIService<T extends ServiceContext = ServiceContext> extends ServiceBase<T> implements IUIService {
|
|
16
|
+
readonly commands: UICommandRegistry;
|
|
15
17
|
private _dialogManager;
|
|
16
18
|
protected _APIService: IAPIService;
|
|
17
19
|
abstract get dialogToCopy(): ComponentHasResult<DialogResult, DialogParams>;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { EVENT_PLUGIN_UNLOADED } from "../../../events/coreEvents.js";
|
|
2
|
+
import { UICommandRegistry } from "../../../uiCommands.js";
|
|
1
3
|
import { ServiceBase } from "../../base/ServiceBase.js";
|
|
2
4
|
class UIService extends ServiceBase {
|
|
5
|
+
commands;
|
|
3
6
|
_dialogManager;
|
|
4
7
|
_APIService;
|
|
5
8
|
constructor(context, dependents) {
|
|
6
9
|
super(context);
|
|
10
|
+
this.commands = new UICommandRegistry();
|
|
11
|
+
this.context.events.once(EVENT_PLUGIN_UNLOADED, () => this.commands.dispose());
|
|
7
12
|
this._dialogManager = dependents.dialogManager;
|
|
8
13
|
this._APIService = dependents.APIService;
|
|
9
14
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IConfigService } from "../../base/IService.js";
|
|
2
2
|
import { ControlService } from "../../base/ControlService.js";
|
|
3
3
|
import type { KeyValueDBService } from "../../base/KeyValueDBService.js";
|
|
4
4
|
import { PathService } from "../../base/PathService.js";
|
|
@@ -35,7 +35,7 @@ export declare class InjectableServiceHub<T extends ServiceContext = ServiceCont
|
|
|
35
35
|
protected readonly _vault: InjectableVaultService<T>;
|
|
36
36
|
protected readonly _test: InjectableTestService<T>;
|
|
37
37
|
protected readonly _ui: UIService<T>;
|
|
38
|
-
protected readonly _config:
|
|
38
|
+
protected readonly _config: IConfigService;
|
|
39
39
|
protected readonly _keyValueDB: KeyValueDBService<T>;
|
|
40
40
|
protected readonly _control: ControlService<T>;
|
|
41
41
|
get API(): InjectableAPIService<T>;
|
|
@@ -55,14 +55,14 @@ export declare class InjectableServiceHub<T extends ServiceContext = ServiceCont
|
|
|
55
55
|
get control(): ControlService<T>;
|
|
56
56
|
get keyValueDB(): KeyValueDBService<T>;
|
|
57
57
|
get UI(): UIService<T>;
|
|
58
|
-
get config():
|
|
58
|
+
get config(): IConfigService;
|
|
59
59
|
constructor(context: T, services: InjectableServiceInstances<T> & {
|
|
60
60
|
setting: SettingService<T>;
|
|
61
61
|
appLifecycle: AppLifecycleServiceBase<T>;
|
|
62
62
|
path: PathService<T>;
|
|
63
63
|
API: InjectableAPIService<T>;
|
|
64
64
|
ui: UIService<T>;
|
|
65
|
-
config:
|
|
65
|
+
config: IConfigService;
|
|
66
66
|
database: DatabaseService<T>;
|
|
67
67
|
remote: InjectableRemoteService<T>;
|
|
68
68
|
vault: InjectableVaultService<T>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { IConfigService } from "../../base/IService.js";
|
|
1
2
|
import { type ServiceInstances } from "../../ServiceHub.js";
|
|
2
3
|
import type { UIService } from "../base/UIService.js";
|
|
3
|
-
import type { ConfigService } from "../../base/ConfigService.js";
|
|
4
4
|
import type { ServiceContext } from "../../base/ServiceBase.js";
|
|
5
5
|
import type { InjectableAPIService } from "./InjectableAPIService.js";
|
|
6
6
|
import type { InjectableDatabaseEventService } from "./InjectableDatabaseEventService.js";
|
|
@@ -32,5 +32,5 @@ export type InjectableServiceInstances<T extends ServiceContext> = ServiceInstan
|
|
|
32
32
|
vault?: InjectableVaultService<T>;
|
|
33
33
|
test?: InjectableTestService<T>;
|
|
34
34
|
ui?: UIService<T>;
|
|
35
|
-
config?:
|
|
35
|
+
config?: IConfigService;
|
|
36
36
|
};
|
|
@@ -4,9 +4,6 @@ import type { ObsidianLiveSyncSettings } from "../../../common/types.js";
|
|
|
4
4
|
import type { PersistedSettings } from "../../../common/models/setting.policy.js";
|
|
5
5
|
export declare class InjectableSettingService<T extends ServiceContext> extends SettingService<T> {
|
|
6
6
|
constructor(context: T, dependencies: SettingServiceDependencies);
|
|
7
|
-
protected setItem(key: string, value: string): void;
|
|
8
|
-
protected getItem(key: string): string;
|
|
9
|
-
protected deleteItem(key: string): void;
|
|
10
7
|
saveData: import("../../lib/HandlerUtils.js").HandlerFunction<(data: PersistedSettings) => Promise<void>, unknown>;
|
|
11
8
|
loadData: import("../../lib/HandlerUtils.js").HandlerFunction<() => Promise<ObsidianLiveSyncSettings | undefined>, unknown>;
|
|
12
9
|
}
|
|
@@ -2,7 +2,6 @@ import { EVENT_SETTING_SAVED } from "../../../events/coreEvents.js";
|
|
|
2
2
|
import { SettingService } from "../../base/SettingService.js";
|
|
3
3
|
import { EVENT_REQUEST_RELOAD_SETTING_TAB } from "../../../events/coreEvents.js";
|
|
4
4
|
import { handlers } from "../../lib/HandlerUtils.js";
|
|
5
|
-
import { compatGlobal } from "../../../common/coreEnvFunctions.js";
|
|
6
5
|
class InjectableSettingService extends SettingService {
|
|
7
6
|
constructor(context, dependencies) {
|
|
8
7
|
super(context, dependencies);
|
|
@@ -15,16 +14,6 @@ class InjectableSettingService extends SettingService {
|
|
|
15
14
|
return Promise.resolve(true);
|
|
16
15
|
});
|
|
17
16
|
}
|
|
18
|
-
setItem(key, value) {
|
|
19
|
-
return compatGlobal.localStorage.setItem(key, value);
|
|
20
|
-
}
|
|
21
|
-
getItem(key) {
|
|
22
|
-
return compatGlobal.localStorage.getItem(key) ?? "";
|
|
23
|
-
}
|
|
24
|
-
deleteItem(key) {
|
|
25
|
-
compatGlobal.localStorage.removeItem(key);
|
|
26
|
-
}
|
|
27
|
-
// override currentSettings = handlers<SettingService<T>>().binder("currentSettings");
|
|
28
17
|
saveData = handlers().binder("saveData");
|
|
29
18
|
loadData = handlers().binder("loadData");
|
|
30
19
|
}
|
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
* A function type that can be used as a handler.
|
|
3
3
|
*/
|
|
4
4
|
type HandlerFunc<TArg extends unknown[], TResult> = (...args: TArg) => TResult | Promise<TResult>;
|
|
5
|
+
/**
|
|
6
|
+
* The outcome of a handler.
|
|
7
|
+
*
|
|
8
|
+
* A completed outcome carries a value when `TValue` is not void, and a veto
|
|
9
|
+
* carries a reason when `TVetoReason` is not void. Not-applicable outcomes
|
|
10
|
+
* always continue to the next registered handler.
|
|
11
|
+
*/
|
|
12
|
+
export type HandlerOutcome<TValue = void, TVetoReason = void> = {
|
|
13
|
+
kind: "not-applicable";
|
|
14
|
+
} | ([TValue] extends [void] ? {
|
|
15
|
+
kind: "completed";
|
|
16
|
+
} : {
|
|
17
|
+
kind: "completed";
|
|
18
|
+
value: TValue;
|
|
19
|
+
}) | ([TVetoReason] extends [void] ? {
|
|
20
|
+
kind: "veto";
|
|
21
|
+
} : {
|
|
22
|
+
kind: "veto";
|
|
23
|
+
value: TVetoReason;
|
|
24
|
+
});
|
|
25
|
+
type AnyHandlerOutcome = HandlerOutcome | HandlerOutcome<unknown, unknown>;
|
|
5
26
|
/**
|
|
6
27
|
* A function type that returns a boolean or a Promise of boolean.
|
|
7
28
|
*/
|
|
@@ -244,6 +265,29 @@ export declare class FirstResultHandler<T extends unknown[], U> extends MultiBin
|
|
|
244
265
|
*/
|
|
245
266
|
invoke(...args: T): Promise<U | false>;
|
|
246
267
|
}
|
|
268
|
+
/**
|
|
269
|
+
* Invokes registered handlers in priority and registration order until one
|
|
270
|
+
* completes or vetoes. Each invocation uses a snapshot of registrations.
|
|
271
|
+
* Callback errors and rejections propagate unchanged, and registrations are
|
|
272
|
+
* owned by this handler.
|
|
273
|
+
*/
|
|
274
|
+
export declare class FirstHandledHandler<T extends unknown[], U extends AnyHandlerOutcome = HandlerOutcome> extends MultiBinder<HandlerFunc<T, U>> implements InvokableHandler<T, U | {
|
|
275
|
+
kind: "not-applicable";
|
|
276
|
+
}> {
|
|
277
|
+
/**
|
|
278
|
+
* Whether at least one callback is currently registered.
|
|
279
|
+
*/
|
|
280
|
+
get hasHandlers(): boolean;
|
|
281
|
+
/**
|
|
282
|
+
* Invokes handlers until one returns a completed or veto outcome.
|
|
283
|
+
* @param args The arguments to pass to the handlers.
|
|
284
|
+
* @returns The first handled outcome, or a not-applicable outcome when no handler applies.
|
|
285
|
+
* @throws Any synchronous callback error or rejected callback value, unchanged.
|
|
286
|
+
*/
|
|
287
|
+
invoke(...args: T): Promise<U | {
|
|
288
|
+
kind: "not-applicable";
|
|
289
|
+
}>;
|
|
290
|
+
}
|
|
247
291
|
/**
|
|
248
292
|
* A function type that can be used as a handler with assignable functionality.
|
|
249
293
|
*/
|
|
@@ -297,6 +341,11 @@ export interface MultipleHandlerFunction<TFunc extends (...args: any[]) => U | P
|
|
|
297
341
|
*/
|
|
298
342
|
removeHandler: (callback: TFunc) => void;
|
|
299
343
|
}
|
|
344
|
+
export type FirstHandledFunction<TFunc extends (...args: any[]) => AnyHandlerOutcome | Promise<AnyHandlerOutcome>> = ((...args: Parameters<TFunc>) => Promise<Awaited<ReturnType<TFunc>> | {
|
|
345
|
+
kind: "not-applicable";
|
|
346
|
+
}>) & Pick<MultipleHandlerFunction<TFunc>, "addHandler" | "removeHandler"> & {
|
|
347
|
+
readonly hasHandlers: boolean;
|
|
348
|
+
};
|
|
300
349
|
/**
|
|
301
350
|
* A function type that can be used as a value-collecting handler with add/remove functionality.
|
|
302
351
|
*/
|
|
@@ -351,12 +400,16 @@ export declare function bailFirstFailureWithResultFunction<TFunc extends (...arg
|
|
|
351
400
|
export declare function allParallelFunction<TFunc extends (...args: any[]) => Promise<boolean>>(name?: string): BooleanMultipleHandlerFunction<TFunc>;
|
|
352
401
|
export declare function anySuccessFunction<TFunc extends (...args: any[]) => Promise<boolean>>(name?: string): BooleanMultipleHandlerFunction<TFunc>;
|
|
353
402
|
export declare function firstResultFunction<TFunc extends (...args: any[]) => Promise<unknown>>(name?: string): MultipleHandlerFunction<TFunc>;
|
|
403
|
+
export declare function firstHandledFunction<TFunc extends (...args: any[]) => AnyHandlerOutcome | Promise<AnyHandlerOutcome>>(name?: string): FirstHandledFunction<TFunc>;
|
|
354
404
|
export declare function dispatchParallelFunction<TFunc extends (...args: any[]) => Promise<unknown[]>>(name?: string): CollectiveHandlerFunction<TFunc>;
|
|
355
405
|
export declare function bindableFunction<TFunc extends (...args: any[]) => any>(name?: string): HandlerFunction<TFunc>;
|
|
356
406
|
export declare function lazyBindableFunction<TFunc extends (...args: any[]) => any>(name?: string): LazyHandlerFunction<TFunc>;
|
|
357
407
|
type FunctionKeys<T> = Extract<{
|
|
358
408
|
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
|
|
359
409
|
}[keyof T], string>;
|
|
410
|
+
type FirstHandledFunctionKeys<T> = Extract<{
|
|
411
|
+
[K in keyof T]: [NonNullable<T[K]>] extends [never] ? never : NonNullable<T[K]> extends (...args: any[]) => AnyHandlerOutcome | Promise<AnyHandlerOutcome> ? K : never;
|
|
412
|
+
}[keyof T], string>;
|
|
360
413
|
export declare function handlers<T extends object>(): {
|
|
361
414
|
/**
|
|
362
415
|
* Create a handler that invokes all added handler functions sequentially until one returns false.
|
|
@@ -403,6 +456,13 @@ export declare function handlers<T extends object>(): {
|
|
|
403
456
|
* @returns
|
|
404
457
|
*/
|
|
405
458
|
firstResult<K extends FunctionKeys<T>>(name: K): MultipleHandlerFunction<Extract<T[K], (...args: any[]) => Promise<unknown>>>;
|
|
459
|
+
/**
|
|
460
|
+
* Create a handler that invokes registered handlers until one completes or vetoes.
|
|
461
|
+
* Callback errors and rejections propagate unchanged.
|
|
462
|
+
* @param name The name of the callback on `T`.
|
|
463
|
+
* @returns A bound handler with a live `hasHandlers` property.
|
|
464
|
+
*/
|
|
465
|
+
firstHandled<K extends FirstHandledFunctionKeys<T>>(name: K): FirstHandledFunction<Extract<NonNullable<T[K]>, (...args: any[]) => AnyHandlerOutcome | Promise<AnyHandlerOutcome>>>;
|
|
406
466
|
/**
|
|
407
467
|
* Create a handler that invokes all added handler functions in parallel.
|
|
408
468
|
* @param name
|