@vrtmrz/livesync-commonlib 0.2.1 → 0.2.3
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 +60 -0
- package/dist/API/processSetting.d.ts +1 -0
- package/dist/ContentSplitter/ContentSplitterRabinKarp.js +2 -17
- package/dist/ContentSplitter/ContentSplitterV1.js +9 -19
- package/dist/ContentSplitter/ContentSplitterV2.js +9 -19
- package/dist/common/models/setting.const.defaults.js +1 -0
- package/dist/common/models/setting.const.qr.js +2 -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 +4 -0
- package/dist/common/models/tweak.definition.d.ts +1 -0
- package/dist/common/settingConstants.d.ts +1 -0
- package/dist/common/settingConstants.js +4 -0
- package/dist/common/utils.d.ts +33 -3
- package/dist/common/utils.js +12 -44
- package/dist/common/utils.object.js +2 -1
- package/dist/concurrency/keyedPendingWork.d.ts +13 -0
- package/dist/concurrency/keyedPendingWork.js +27 -0
- package/dist/conflicts/conflictCheckQueue.d.ts +45 -0
- package/dist/conflicts/conflictCheckQueue.js +108 -0
- package/dist/conflicts.d.ts +3 -0
- package/dist/conflicts.js +4 -0
- package/dist/diagnostics/integrity.js +11 -12
- package/dist/interfaces/FileHandler.d.ts +2 -0
- package/dist/interfaces/ServiceModule.d.ts +42 -24
- package/dist/interfaces/ServiceModule.js +4 -3
- package/dist/interfaces/StorageAccess.d.ts +2 -0
- package/dist/interfaces/StorageEventManager.d.ts +2 -0
- package/dist/interfaces/StorageEventManager.js +4 -0
- package/dist/managers/StorageEventManager.d.ts +4 -3
- package/dist/managers/StorageEventManager.js +24 -6
- package/dist/pouchdb/LiveSyncLocalDB.js +1 -0
- package/dist/pouchdb/chunks.d.ts +16 -0
- package/dist/pouchdb/pouchdb-browser.js +2 -84
- package/dist/pouchdb/pouchdb-http.js +2 -84
- package/dist/pouchdb/purgeMulti.d.ts +4 -0
- package/dist/pouchdb/purgeMulti.js +89 -0
- package/dist/replication/journal/JournalSyncCore.js +6 -1
- package/dist/replication/trystero/TrysteroReplicator.d.ts +2 -0
- package/dist/replication/trystero/TrysteroReplicator.js +3 -4
- package/dist/replication/trystero/TrysteroReplicatorP2PServer.js +3 -2
- package/dist/replication/trystero/peerPatterns.d.ts +1 -0
- package/dist/replication/trystero/peerPatterns.js +6 -0
- package/dist/rpc/RpcRoom.d.ts +5 -1
- package/dist/rpc/RpcRoom.js +80 -105
- package/dist/serviceFeatures/offlineScanner.d.ts +8 -8
- package/dist/serviceFeatures/offlineScanner.js +25 -21
- package/dist/serviceFeatures/prepareDatabaseForUse.d.ts +4 -3
- package/dist/serviceFeatures/prepareDatabaseForUse.js +5 -4
- package/dist/serviceFeatures/remoteConfig.d.ts +10 -5
- package/dist/serviceModules/ServiceFileAccessBase.d.ts +1 -0
- package/dist/serviceModules/ServiceFileAccessBase.js +3 -0
- package/dist/serviceModules/ServiceFileHandlerBase.d.ts +1 -0
- package/dist/serviceModules/ServiceFileHandlerBase.js +23 -7
- package/dist/services/base/DatabaseEventService.d.ts +1 -1
- package/dist/services/base/IService.d.ts +3 -3
- package/dist/services/base/VaultScanResult.d.ts +5 -0
- package/dist/services/base/VaultService.d.ts +1 -1
- package/dist/services/implements/injectable/InjectableConflictService.d.ts +5 -5
- package/dist/services/lib/HandlerUtils.d.ts +8 -6
- package/dist/services/lib/HandlerUtils.js +14 -0
- package/dist/worker/bgWorker.js +1 -1
- package/docs/conflict-resolution.md +30 -0
- package/docs/development.md +46 -0
- package/docs/offline-scanner.md +12 -4
- package/docs/proven-in-use.md +63 -0
- package/docs/releasing.md +2 -0
- package/docs/service-feature-composition.md +56 -9
- package/docs/storage-events-and-reflection.md +6 -2
- package/package.json +10 -2
|
@@ -3,6 +3,10 @@ import { type RemoteConfigurationResult } from "../common/ConnectionString.js";
|
|
|
3
3
|
import type { ObsidianLiveSyncSettings, RemoteConfiguration } from "../common/models/setting.type.js";
|
|
4
4
|
import type { NecessaryServices } from "../interfaces/ServiceModule.js";
|
|
5
5
|
export type RemoteConfigHost = NecessaryServices<"setting" | "UI" | "replication" | "control" | "appLifecycle" | "API", never>;
|
|
6
|
+
type RemoteConfigMigrationHost = NecessaryServices<"setting" | "API", never>;
|
|
7
|
+
type RemoteConfigMigrationFeatureHost = NecessaryServices<"setting" | "API" | "appLifecycle", never>;
|
|
8
|
+
type RemoteConfigSwitchHost = NecessaryServices<"setting" | "UI" | "control" | "API", never>;
|
|
9
|
+
type RemoteConfigReplicationHost = NecessaryServices<"setting" | "UI" | "replication" | "control" | "API", never>;
|
|
6
10
|
export declare function migrateLegacyRemoteConfigurationsInPlace(settings: ObsidianLiveSyncSettings, log?: (message: string, level?: LOG_LEVEL) => void): boolean;
|
|
7
11
|
/**
|
|
8
12
|
* Generate a unique ID for a new remote configuration.
|
|
@@ -54,7 +58,7 @@ export declare function migrateP2PActiveRemoteConfigurationIdInPlace(settings: O
|
|
|
54
58
|
/**
|
|
55
59
|
* Migrates existing flat settings to the new multiple remote configurations list.
|
|
56
60
|
*/
|
|
57
|
-
export declare function migrateToMultipleRemoteConfigurations(host:
|
|
61
|
+
export declare function migrateToMultipleRemoteConfigurations(host: RemoteConfigMigrationHost): Promise<boolean>;
|
|
58
62
|
/**
|
|
59
63
|
* Logic to switch the active configuration.
|
|
60
64
|
*/
|
|
@@ -67,16 +71,17 @@ export declare function activateP2PRemoteConfiguration(settings: ObsidianLiveSyn
|
|
|
67
71
|
/**
|
|
68
72
|
* Command: Switch active connection
|
|
69
73
|
*/
|
|
70
|
-
export declare function commandSwitchActiveRemote(host:
|
|
74
|
+
export declare function commandSwitchActiveRemote(host: RemoteConfigSwitchHost): Promise<void>;
|
|
71
75
|
/**
|
|
72
76
|
* Command: Sync with a saved connection
|
|
73
77
|
*/
|
|
74
|
-
export declare function commandReplicateWithSpecificRemote(host:
|
|
78
|
+
export declare function commandReplicateWithSpecificRemote(host: RemoteConfigReplicationHost): Promise<void>;
|
|
75
79
|
/**
|
|
76
80
|
* Migration feature to be used during initialisation.
|
|
77
81
|
*/
|
|
78
|
-
export declare function useRemoteConfigurationMigration(host:
|
|
82
|
+
export declare function useRemoteConfigurationMigration(host: RemoteConfigMigrationFeatureHost): void;
|
|
79
83
|
/**
|
|
80
84
|
* Hook to set up remote configuration features (Commands).
|
|
81
85
|
*/
|
|
82
|
-
export declare function useRemoteConfiguration(host:
|
|
86
|
+
export declare function useRemoteConfiguration(host: RemoteConfigReplicationHost): boolean;
|
|
87
|
+
export {};
|
|
@@ -26,6 +26,7 @@ export declare class ServiceFileAccessBase<TAdapter extends IFileSystemAdapter<a
|
|
|
26
26
|
private vault;
|
|
27
27
|
private setting;
|
|
28
28
|
constructor(services: StorageAccessBaseDependencies<TAdapter>);
|
|
29
|
+
hasPendingFileEvent(path: FilePath): boolean;
|
|
29
30
|
restoreState(): Promise<void>;
|
|
30
31
|
_everyOnFirstInitialize(): Promise<boolean>;
|
|
31
32
|
_everyCommitPendingFileEvent(): Promise<boolean>;
|
|
@@ -98,6 +98,7 @@ export declare abstract class ServiceFileHandlerBase extends ServiceModuleBase<S
|
|
|
98
98
|
getPath(entry: AnyEntry): FilePathWithPrefix;
|
|
99
99
|
getPathWithoutPrefix(entry: AnyEntry): FilePathWithPrefix;
|
|
100
100
|
readFileFromStub(file: UXFileInfoStub | UXFileInfo): Promise<UXFileInfo>;
|
|
101
|
+
canSkipUnchangedFile(info: UXFileInfoStub, expected: MetaEntry): Promise<boolean>;
|
|
101
102
|
tryRecordUntrackedFileRevision(info: UXFileInfoStub | UXFileInfo, expectedRevision: string): Promise<boolean>;
|
|
102
103
|
private infoToStub;
|
|
103
104
|
storeFileToDB(info: UXFileInfoStub | UXFileInfo | UXInternalFileInfoStub | FilePathWithPrefix, force?: boolean, onlyChunks?: boolean, options?: StoreFileOptions): Promise<boolean>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
|
2
|
-
import {
|
|
2
|
+
import { withLocks } from "octagonal-wheels/concurrency/lock";
|
|
3
3
|
import {
|
|
4
4
|
createBlob,
|
|
5
5
|
getDocDataAsArray,
|
|
@@ -15,11 +15,6 @@ import { getStoragePathFromUXFileInfo } from "../common/typeUtils.js";
|
|
|
15
15
|
import { EVEN } from "../common/models/shared.const.symbols.js";
|
|
16
16
|
import { tryGetFilePath } from "../common/utils.doc.js";
|
|
17
17
|
import { getPathNormalizationVersion, normalisePathForIdentity } from "../common/pathNormalization.js";
|
|
18
|
-
async function serializedByKeys(keys, callback) {
|
|
19
|
-
const [key, ...remainingKeys] = keys;
|
|
20
|
-
if (key === void 0) return await callback();
|
|
21
|
-
return await serialized(key, () => serializedByKeys(remainingKeys, callback));
|
|
22
|
-
}
|
|
23
18
|
function getParentPath(path) {
|
|
24
19
|
const lastSeparator = path.lastIndexOf("/");
|
|
25
20
|
return lastSeparator < 0 ? "" : path.slice(0, lastSeparator);
|
|
@@ -258,6 +253,27 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
258
253
|
}
|
|
259
254
|
return readFile;
|
|
260
255
|
}
|
|
256
|
+
async canSkipUnchangedFile(info, expected) {
|
|
257
|
+
const path = getStoragePathFromUXFileInfo(info);
|
|
258
|
+
const provenance = this.fileReflectionProvenance;
|
|
259
|
+
if (!provenance || !expected._rev || expected.deleted || expected._deleted || expected._conflicts?.length || this.storage.hasPendingFileEvent?.(path) !== false) return false;
|
|
260
|
+
try {
|
|
261
|
+
return await this.serializedByFileEventPaths([path], async () => {
|
|
262
|
+
const provenancePath = this.getProvenancePath(path);
|
|
263
|
+
const record = await provenance.get(provenancePath);
|
|
264
|
+
if (!record || !hasContentHash(record) || record.revision !== expected._rev || !Number.isFinite(record.observedStorageMtime) || record.observedStorageMtime !== info.stat.mtime || record.contentSize !== info.stat.size) return false;
|
|
265
|
+
const current = await this.db.fetchEntryMeta(path, void 0, true);
|
|
266
|
+
if (!current || current._rev !== record.revision || current.deleted || current._deleted || (await this.db.getConflictedRevs(path)).length > 0) return false;
|
|
267
|
+
const stat = await this.storage.stat(path);
|
|
268
|
+
if (!stat || stat.type !== "file" || stat.mtime !== record.observedStorageMtime || stat.size !== record.contentSize) return false;
|
|
269
|
+
return sameProvenance(await provenance.get(provenancePath), record) && this.storage.hasPendingFileEvent?.(path) === false;
|
|
270
|
+
});
|
|
271
|
+
} catch (error) {
|
|
272
|
+
this._log(`Could not inspect unchanged file metadata for ${path}`, LOG_LEVEL_VERBOSE);
|
|
273
|
+
this._log(error, LOG_LEVEL_VERBOSE);
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
261
277
|
async tryRecordUntrackedFileRevision(info, expectedRevision) {
|
|
262
278
|
if (!this.fileReflectionProvenance || !expectedRevision) return false;
|
|
263
279
|
const path = getStoragePathFromUXFileInfo(info);
|
|
@@ -1179,7 +1195,7 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
1179
1195
|
let failed = false;
|
|
1180
1196
|
let failure;
|
|
1181
1197
|
try {
|
|
1182
|
-
result = await
|
|
1198
|
+
result = await withLocks(lockKeys, async () => {
|
|
1183
1199
|
for (const id of uniqueIds) this.deferredConflictChecks.set(id, pending);
|
|
1184
1200
|
try {
|
|
1185
1201
|
return await callback(documentIds.length === 2 && documentIds[0] === documentIds[1]);
|
|
@@ -34,5 +34,5 @@ export declare abstract class DatabaseEventService<T extends ServiceContext = Se
|
|
|
34
34
|
* @param reopenDatabase Whether to reopen the database if it is already open.
|
|
35
35
|
* @param ignoreSuspending Whether to ignore any suspending state.
|
|
36
36
|
*/
|
|
37
|
-
readonly initialiseDatabase: import("../lib/HandlerUtils.js").MultipleHandlerFunction<(showingNotice?: boolean, reopenDatabase?: boolean, ignoreSuspending?: boolean, continueOnFileFailure?: boolean) => Promise<import("./VaultScanResult.js").VaultScanResult>, unknown>;
|
|
37
|
+
readonly initialiseDatabase: import("../lib/HandlerUtils.js").MultipleHandlerFunction<(showingNotice?: boolean, reopenDatabase?: boolean, ignoreSuspending?: boolean, continueOnFileFailure?: boolean, scanOptions?: import("./VaultScanResult.js").VaultScanOptions) => Promise<import("./VaultScanResult.js").VaultScanResult>, unknown>;
|
|
38
38
|
}
|
|
@@ -17,7 +17,7 @@ import type { ActiveReplicatorContext, ContinuousReplicationRequest, Replication
|
|
|
17
17
|
import type { RemoteResourceKind, RemoteResourceMap } from "../../replication/RemoteResource.js";
|
|
18
18
|
import type { CentralRemoteAdministrationRequest, CentralRemoteAdministrationResult } from "../../replication/CentralRemoteAdministration.js";
|
|
19
19
|
import type { HandlerOutcome, MultipleHandlerFunction } from "../lib/HandlerUtils.js";
|
|
20
|
-
import type { VaultScanResult } from "./VaultScanResult.js";
|
|
20
|
+
import type { VaultScanResult, VaultScanOptions } from "./VaultScanResult.js";
|
|
21
21
|
import type { UICommandRegistry } from "../../uiCommands.js";
|
|
22
22
|
declare global {
|
|
23
23
|
interface OPTIONAL_SYNC_FEATURES {
|
|
@@ -91,7 +91,7 @@ export interface IDatabaseEventService {
|
|
|
91
91
|
onDatabaseInitialised(showNotice: boolean): Promise<boolean>;
|
|
92
92
|
onDatabaseHasReady(): Promise<boolean>;
|
|
93
93
|
onResetDatabase(db: LiveSyncLocalDB): Promise<boolean>;
|
|
94
|
-
initialiseDatabase(showingNotice?: boolean, reopenDatabase?: boolean, ignoreSuspending?: boolean, continueOnFileFailure?: boolean): Promise<VaultScanResult>;
|
|
94
|
+
initialiseDatabase(showingNotice?: boolean, reopenDatabase?: boolean, ignoreSuspending?: boolean, continueOnFileFailure?: boolean, scanOptions?: VaultScanOptions): Promise<VaultScanResult>;
|
|
95
95
|
}
|
|
96
96
|
export interface IKeyValueDBService {
|
|
97
97
|
openSimpleStore<T>(kind: string): SimpleStore<T>;
|
|
@@ -355,7 +355,7 @@ export interface ITweakValueService {
|
|
|
355
355
|
export interface IVaultService {
|
|
356
356
|
vaultName(): string;
|
|
357
357
|
getVaultName(): string;
|
|
358
|
-
scanVault(showingNotice?: boolean, ignoreSuspending?: boolean, continueOnFileFailure?: boolean): Promise<VaultScanResult>;
|
|
358
|
+
scanVault(showingNotice?: boolean, ignoreSuspending?: boolean, continueOnFileFailure?: boolean, scanOptions?: VaultScanOptions): Promise<VaultScanResult>;
|
|
359
359
|
isIgnoredByIgnoreFile(file: string | UXFileInfoStub): Promise<boolean>;
|
|
360
360
|
isTargetFile(file: string | UXFileInfoStub,
|
|
361
361
|
/** Inspect selection policy without treating a filename collision as an exclusion. */
|
|
@@ -10,4 +10,9 @@ export declare const VaultScanResults: {
|
|
|
10
10
|
readonly COMPLETED: true;
|
|
11
11
|
readonly COMPLETED_WITH_FILE_FAILURES: "completed-with-file-failures";
|
|
12
12
|
};
|
|
13
|
+
/** Optional policy for an ordinary scan; omitted options retain strict content checks. */
|
|
14
|
+
export interface VaultScanOptions {
|
|
15
|
+
/** False permits unchanged tracked files to be skipped using storage and revision metadata. */
|
|
16
|
+
strictContentCheck?: boolean;
|
|
17
|
+
}
|
|
13
18
|
export type VaultScanResult = (typeof VaultScanResults)[keyof typeof VaultScanResults];
|
|
@@ -26,7 +26,7 @@ export declare abstract class VaultService<T extends ServiceContext = ServiceCon
|
|
|
26
26
|
* @param showingNotice Whether to show a notice to the user.
|
|
27
27
|
* @param ignoreSuspending Whether to ignore any suspending state.
|
|
28
28
|
*/
|
|
29
|
-
readonly scanVault: import("../lib/HandlerUtils.js").MultipleHandlerFunction<(showingNotice?: boolean, ignoreSuspending?: boolean, continueOnFileFailure?: boolean) => Promise<import("./VaultScanResult.js").VaultScanResult>, unknown>;
|
|
29
|
+
readonly scanVault: import("../lib/HandlerUtils.js").MultipleHandlerFunction<(showingNotice?: boolean, ignoreSuspending?: boolean, continueOnFileFailure?: boolean, scanOptions?: import("./VaultScanResult.js").VaultScanOptions) => Promise<import("./VaultScanResult.js").VaultScanResult>, unknown>;
|
|
30
30
|
/**
|
|
31
31
|
* Check if a file is ignored by the ignore file (e.g., .gitignore, .obsidianignore).
|
|
32
32
|
* @param file The file path or file info stub to check.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ConflictService } from "../../base/ConflictService.js";
|
|
2
2
|
import type { ServiceContext } from "../../base/ServiceBase.js";
|
|
3
3
|
export declare class InjectableConflictService<T extends ServiceContext> extends ConflictService<T> {
|
|
4
|
-
queueCheckForIfOpen: import("../../lib/HandlerUtils.js").HandlerFunction<(path: import("../../../
|
|
5
|
-
queueCheckFor: import("../../lib/HandlerUtils.js").HandlerFunction<(path: import("../../../
|
|
4
|
+
queueCheckForIfOpen: import("../../lib/HandlerUtils.js").HandlerFunction<(path: import("../../../conflicts.js").FilePathWithPrefix) => Promise<void>, unknown>;
|
|
5
|
+
queueCheckFor: import("../../lib/HandlerUtils.js").HandlerFunction<(path: import("../../../conflicts.js").FilePathWithPrefix) => Promise<void>, unknown>;
|
|
6
6
|
ensureAllProcessed: import("../../lib/HandlerUtils.js").HandlerFunction<() => Promise<boolean>, unknown>;
|
|
7
|
-
resolveByDeletingRevision: import("../../lib/HandlerUtils.js").HandlerFunction<(path: import("../../../
|
|
8
|
-
resolve: import("../../lib/HandlerUtils.js").HandlerFunction<(filename: import("../../../
|
|
9
|
-
resolveByNewest: import("../../lib/HandlerUtils.js").HandlerFunction<(filename: import("../../../
|
|
7
|
+
resolveByDeletingRevision: import("../../lib/HandlerUtils.js").HandlerFunction<(path: import("../../../conflicts.js").FilePathWithPrefix, deleteRevision: string, title: string) => Promise<typeof import("../../../common/types.js").MISSING_OR_ERROR | typeof import("../../../common/types.js").AUTO_MERGED>, unknown>;
|
|
8
|
+
resolve: import("../../lib/HandlerUtils.js").HandlerFunction<(filename: import("../../../conflicts.js").FilePathWithPrefix) => Promise<void>, unknown>;
|
|
9
|
+
resolveByNewest: import("../../lib/HandlerUtils.js").HandlerFunction<(filename: import("../../../conflicts.js").FilePathWithPrefix) => Promise<boolean>, unknown>;
|
|
10
10
|
resolveAllConflictedFilesByNewerOnes: import("../../lib/HandlerUtils.js").HandlerFunction<() => Promise<void>, unknown>;
|
|
11
11
|
}
|
|
@@ -94,11 +94,12 @@ export declare class Binder<T extends (...args: any[]) => any> implements Binder
|
|
|
94
94
|
*/
|
|
95
95
|
constructor(name: string, initialCallback?: T);
|
|
96
96
|
private _callback;
|
|
97
|
+
private _registrationToken;
|
|
97
98
|
/**
|
|
98
99
|
* Assigns a new handler function.
|
|
99
100
|
* @param callback The new handler function to assign.
|
|
100
101
|
*/
|
|
101
|
-
assign(callback: T, override?: boolean):
|
|
102
|
+
assign(callback: T, override?: boolean): UnregisterFunction;
|
|
102
103
|
/**
|
|
103
104
|
* Invokes the assigned handler function with the provided arguments.
|
|
104
105
|
* @param args The arguments to pass to the handler function.
|
|
@@ -114,13 +115,14 @@ export declare class LazyBinder<T extends (...args: any[]) => any> implements Bi
|
|
|
114
115
|
private _name;
|
|
115
116
|
private _callbackPromise;
|
|
116
117
|
private _callback;
|
|
118
|
+
private _registrationToken;
|
|
117
119
|
/**
|
|
118
120
|
* Creates a new LazyBinder instance.
|
|
119
121
|
* @param name The name of the handler.
|
|
120
122
|
* @param initialCallback An optional initial callback function to assign.
|
|
121
123
|
*/
|
|
122
124
|
constructor(name: string, initialCallback?: T);
|
|
123
|
-
assign(callback: T, override?: boolean):
|
|
125
|
+
assign(callback: T, override?: boolean): UnregisterFunction;
|
|
124
126
|
/**
|
|
125
127
|
* Invokes the assigned handler function with the provided arguments.
|
|
126
128
|
* @param args The arguments to pass to the handler function.
|
|
@@ -300,9 +302,9 @@ export interface HandlerFunction<TFunc extends (...args: any[]) => U | Promise<U
|
|
|
300
302
|
* Assigns a new handler function.
|
|
301
303
|
* @param callback The new handler function to assign.
|
|
302
304
|
* @param override Whether to override the existing handler if one is already assigned.
|
|
303
|
-
* @returns A function
|
|
305
|
+
* @returns A function that unregisters this registration if it is still current.
|
|
304
306
|
*/
|
|
305
|
-
setHandler: (callback: TFunc, override?: boolean) =>
|
|
307
|
+
setHandler: (callback: TFunc, override?: boolean) => UnregisterFunction;
|
|
306
308
|
}
|
|
307
309
|
/**
|
|
308
310
|
* A function type that can be used as a handler with assignable functionality.
|
|
@@ -316,9 +318,9 @@ export interface LazyHandlerFunction<TFunc extends (...args: any[]) => U | Promi
|
|
|
316
318
|
* Assigns a new handler function.
|
|
317
319
|
* @param callback The new handler function to assign.
|
|
318
320
|
* @param override Whether to override the existing handler if one is already assigned.
|
|
319
|
-
* @returns A function
|
|
321
|
+
* @returns A function that unregisters this registration if it is still current.
|
|
320
322
|
*/
|
|
321
|
-
setHandler: (callback: TFunc, override?: boolean) =>
|
|
323
|
+
setHandler: (callback: TFunc, override?: boolean) => UnregisterFunction;
|
|
322
324
|
}
|
|
323
325
|
/**
|
|
324
326
|
* A function type that can be used as a multiple handler with add/remove functionality.
|
|
@@ -14,6 +14,7 @@ class Binder {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
_callback = null;
|
|
17
|
+
_registrationToken = null;
|
|
17
18
|
/**
|
|
18
19
|
* Assigns a new handler function.
|
|
19
20
|
* @param callback The new handler function to assign.
|
|
@@ -22,8 +23,14 @@ class Binder {
|
|
|
22
23
|
if (this._callback && !override) {
|
|
23
24
|
throw new Error(`Handler ${this._name} is already assigned.`);
|
|
24
25
|
}
|
|
26
|
+
const registrationToken = /* @__PURE__ */ Symbol();
|
|
27
|
+
this._registrationToken = registrationToken;
|
|
25
28
|
this._callback = callback;
|
|
26
29
|
return () => {
|
|
30
|
+
if (this._registrationToken !== registrationToken) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
this._registrationToken = null;
|
|
27
34
|
this._callback = null;
|
|
28
35
|
};
|
|
29
36
|
}
|
|
@@ -43,6 +50,7 @@ class LazyBinder {
|
|
|
43
50
|
_name;
|
|
44
51
|
_callbackPromise = promiseWithResolvers();
|
|
45
52
|
_callback = null;
|
|
53
|
+
_registrationToken = null;
|
|
46
54
|
/**
|
|
47
55
|
* Creates a new LazyBinder instance.
|
|
48
56
|
* @param name The name of the handler.
|
|
@@ -59,9 +67,15 @@ class LazyBinder {
|
|
|
59
67
|
if (this._callback && !override) {
|
|
60
68
|
throw new Error(`Handler ${this._name} is already assigned.`);
|
|
61
69
|
}
|
|
70
|
+
const registrationToken = /* @__PURE__ */ Symbol();
|
|
71
|
+
this._registrationToken = registrationToken;
|
|
62
72
|
this._callback = callback;
|
|
63
73
|
this._callbackPromise.resolve();
|
|
64
74
|
return () => {
|
|
75
|
+
if (this._registrationToken !== registrationToken) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this._registrationToken = null;
|
|
65
79
|
this._callback = null;
|
|
66
80
|
this._callbackPromise = promiseWithResolvers();
|
|
67
81
|
};
|