@vrtmrz/livesync-commonlib 0.2.0 → 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 +43 -0
- package/dist/API/processSetting.d.ts +1 -0
- package/dist/common/models/setting.const.defaults.js +1 -0
- 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/StorageEventManager.d.ts +19 -10
- package/dist/managers/StorageEventManager.js +259 -189
- package/dist/replication/LiveSyncAbstractReplicator.d.ts +2 -0
- package/dist/replication/LiveSyncAbstractReplicator.js +5 -2
- package/dist/replication/couchdb/LiveSyncReplicator.d.ts +6 -0
- package/dist/replication/couchdb/LiveSyncReplicator.js +39 -2
- package/dist/replication/couchdb/replicationProgress.d.ts +6 -0
- package/dist/replication/couchdb/replicationProgress.js +105 -0
- package/dist/replication/index.d.ts +1 -0
- package/dist/replication/trystero/TrysteroReplicator.d.ts +2 -0
- package/dist/serviceFeatures/offlineScanner.js +19 -7
- 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/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 +9 -1
- package/dist/services/base/ReplicatorService.d.ts +1 -0
- package/dist/services/lib/HandlerUtils.d.ts +60 -0
- package/dist/services/lib/HandlerUtils.js +46 -0
- package/dist/worker/bgWorker.js +1 -1
- package/docs/offline-scanner.md +15 -13
- package/docs/settings-lifecycle.md +2 -0
- package/docs/storage-events-and-reflection.md +109 -10
- package/package.json +2 -2
|
@@ -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
|
*/
|
|
@@ -16,7 +16,7 @@ import type { OwnedCouchDBConnection, RemoteConnectionOpenOptions } from "./Remo
|
|
|
16
16
|
import type { ActiveReplicatorContext, ContinuousReplicationRequest, ReplicationAttemptFailure, ReplicationFailureRequest, ReplicatorProviderDefinitionMap, ReplicationOutcome, ReplicationReadinessRequirements, UnattendedOneShotRequest, UserInitiatedOneShotRequest } from "../../replication/ReplicatorProvider.js";
|
|
17
17
|
import type { RemoteResourceKind, RemoteResourceMap } from "../../replication/RemoteResource.js";
|
|
18
18
|
import type { CentralRemoteAdministrationRequest, CentralRemoteAdministrationResult } from "../../replication/CentralRemoteAdministration.js";
|
|
19
|
-
import type { MultipleHandlerFunction } from "../lib/HandlerUtils.js";
|
|
19
|
+
import type { HandlerOutcome, MultipleHandlerFunction } from "../lib/HandlerUtils.js";
|
|
20
20
|
import type { VaultScanResult } from "./VaultScanResult.js";
|
|
21
21
|
import type { UICommandRegistry } from "../../uiCommands.js";
|
|
22
22
|
declare global {
|
|
@@ -97,9 +97,17 @@ export interface IKeyValueDBService {
|
|
|
97
97
|
openSimpleStore<T>(kind: string): SimpleStore<T>;
|
|
98
98
|
simpleStore: SimpleStore<unknown>;
|
|
99
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
|
+
}
|
|
100
105
|
export interface IFileProcessingService {
|
|
101
106
|
processFileEvent(item: FileEventItem): Promise<boolean>;
|
|
107
|
+
/** Legacy notification contract; its boolean does not confirm persistence. */
|
|
102
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>>;
|
|
103
111
|
commitPendingFileEvents(): Promise<boolean>;
|
|
104
112
|
batched: ReactiveSource<number>;
|
|
105
113
|
processing: ReactiveSource<number>;
|
|
@@ -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
|
}
|
|
@@ -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
|
|
@@ -326,6 +326,30 @@ class FirstResultHandler extends MultiBinder {
|
|
|
326
326
|
return false;
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
|
+
class FirstHandledHandler extends MultiBinder {
|
|
330
|
+
/**
|
|
331
|
+
* Whether at least one callback is currently registered.
|
|
332
|
+
*/
|
|
333
|
+
get hasHandlers() {
|
|
334
|
+
return this._callbackMap.size > 0;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Invokes handlers until one returns a completed or veto outcome.
|
|
338
|
+
* @param args The arguments to pass to the handlers.
|
|
339
|
+
* @returns The first handled outcome, or a not-applicable outcome when no handler applies.
|
|
340
|
+
* @throws Any synchronous callback error or rejected callback value, unchanged.
|
|
341
|
+
*/
|
|
342
|
+
async invoke(...args) {
|
|
343
|
+
const callbacks = [...this._callbacks];
|
|
344
|
+
for (const callback of callbacks) {
|
|
345
|
+
const outcome = await Promise.resolve(callback(...args));
|
|
346
|
+
if (outcome.kind !== "not-applicable") {
|
|
347
|
+
return outcome;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return { kind: "not-applicable" };
|
|
351
|
+
}
|
|
352
|
+
}
|
|
329
353
|
function getMultipleBound(handler) {
|
|
330
354
|
const _handler = "invoke" in handler ? handler.invoke : handler.dispatch;
|
|
331
355
|
const __handler = _handler.bind(handler);
|
|
@@ -367,6 +391,17 @@ function firstResultFunction(name) {
|
|
|
367
391
|
const handler = new FirstResultHandler(name ?? "firstResultFunction");
|
|
368
392
|
return getMultipleBound(handler);
|
|
369
393
|
}
|
|
394
|
+
function firstHandledFunction(name) {
|
|
395
|
+
const handler = new FirstHandledHandler(
|
|
396
|
+
name ?? "firstHandledFunction"
|
|
397
|
+
);
|
|
398
|
+
const toHandlerCallback = (callback) => callback;
|
|
399
|
+
const bound = (...args) => handler.invoke(...args);
|
|
400
|
+
bound.addHandler = (callback, priority) => handler.addHandler(toHandlerCallback(callback), priority);
|
|
401
|
+
bound.removeHandler = (callback) => handler.removeHandler(toHandlerCallback(callback));
|
|
402
|
+
Object.defineProperty(bound, "hasHandlers", { get: () => handler.hasHandlers });
|
|
403
|
+
return bound;
|
|
404
|
+
}
|
|
370
405
|
function dispatchParallelFunction(name) {
|
|
371
406
|
const handler = new DispatchParallel(
|
|
372
407
|
name ?? "dispatchParallelFunction"
|
|
@@ -452,6 +487,15 @@ function handlers() {
|
|
|
452
487
|
firstResult(name) {
|
|
453
488
|
return firstResultFunction(String(name));
|
|
454
489
|
},
|
|
490
|
+
/**
|
|
491
|
+
* Create a handler that invokes registered handlers until one completes or vetoes.
|
|
492
|
+
* Callback errors and rejections propagate unchanged.
|
|
493
|
+
* @param name The name of the callback on `T`.
|
|
494
|
+
* @returns A bound handler with a live `hasHandlers` property.
|
|
495
|
+
*/
|
|
496
|
+
firstHandled(name) {
|
|
497
|
+
return firstHandledFunction(String(name));
|
|
498
|
+
},
|
|
455
499
|
/**
|
|
456
500
|
* Create a handler that invokes all added handler functions in parallel.
|
|
457
501
|
* @param name
|
|
@@ -481,6 +525,7 @@ export {
|
|
|
481
525
|
BooleanHandlerBase,
|
|
482
526
|
Dispatch,
|
|
483
527
|
DispatchParallel,
|
|
528
|
+
FirstHandledHandler,
|
|
484
529
|
FirstResultHandler,
|
|
485
530
|
LazyBinder,
|
|
486
531
|
MultiBinder,
|
|
@@ -494,6 +539,7 @@ export {
|
|
|
494
539
|
bailFirstFailureWithResultFunction,
|
|
495
540
|
bindableFunction,
|
|
496
541
|
dispatchParallelFunction,
|
|
542
|
+
firstHandledFunction,
|
|
497
543
|
firstResultFunction,
|
|
498
544
|
handlers,
|
|
499
545
|
lazyBindableFunction
|