Complete central provider, resource, scheduling, and recovery contracts

This commit is contained in:
vorotamoroz
2026-08-31 15:06:06 +00:00
parent 24228bf7cf
commit 1d2077d3fc
18 changed files with 464 additions and 34 deletions
@@ -109,7 +109,7 @@ export class ReplicateResultProcessor {
public get isSuspended() {
return (
this._suspended ||
!this.services.appLifecycle.isReady ||
!this.services.appLifecycle.isReady() ||
this.context.currentSettings().suspendParseReplicationResult ||
this.services.appLifecycle.isSuspended()
);
@@ -20,6 +20,7 @@ function note(id: string): PouchDB.Core.ExistingDocument<EntryDoc> {
}
type SetupOptions = {
applicationReady?: boolean;
processSynchroniseResult?: (entry: unknown) => Promise<void>;
setSnapshot?: (key: string, value: unknown) => Promise<unknown>;
};
@@ -29,9 +30,10 @@ function setup(options: SetupOptions = {}) {
const setSnapshot = vi.fn(options.setSnapshot ?? (async () => undefined));
const runBoundedLocalApplicationActivity = vi.fn(async (task: () => Promise<void>) => await task());
const onCloseActiveReplication = vi.fn(async () => true);
const isReady = vi.fn(() => options.applicationReady ?? true);
const core = {
services: {
appLifecycle: { isReady: true, isSuspended: () => false },
appLifecycle: { isReady, isSuspended: () => false },
path: { getPath: (entry: { path: string }) => entry.path },
replication: {
databaseQueueCount: reactiveSource(0),
@@ -65,6 +67,7 @@ function setup(options: SetupOptions = {}) {
services: core.services,
} as never);
return {
isReady,
onCloseActiveReplication,
processor,
processSynchroniseResult,
@@ -73,6 +76,13 @@ function setup(options: SetupOptions = {}) {
}
describe("ReplicateResultProcessor", () => {
it("suspends result application while the application is not ready", () => {
const { isReady, processor } = setup({ applicationReady: false });
expect(processor.isSuspended).toBe(true);
expect(isReady).toHaveBeenCalledOnce();
});
it("retires active ownership when a newer remote version is observed", async () => {
const { onCloseActiveReplication, processor } = setup();
const versionInfo = {
+1 -2
View File
@@ -25,8 +25,7 @@ function ownsLocalApplicationActivity(value: object): value is LocalApplicationA
*
* Registration order is observable for equal-priority handlers. The host must
* call this after host serviceFeatures and add-ons are composed, but before
* legacy modules are bound. This preserves the former ModuleReplicator
* lifecycle-handler order without retaining a public module identity.
* legacy modules are bound, so lifecycle handlers observe the required order.
*/
export function useReplicationFeature<TContext extends ServiceContext, TCommands extends IMinimumLiveSyncCommands>(
core: LiveSyncBaseCore<TContext, TCommands>
+3 -1
View File
@@ -3,6 +3,7 @@ import type { ObsidianLiveSyncSettings } from "@vrtmrz/livesync-commonlib/compat
import type { NecessaryServices } from "@vrtmrz/livesync-commonlib/compat/interfaces/ServiceModule";
import { createInstanceLogFunction } from "@vrtmrz/livesync-commonlib/compat/services/lib/logUtils";
import {
CAPABILITY_UNAVAILABLE_REASONS,
isReplicationCompleted,
NO_INTERACTION,
type ContinuousReplicationRequest,
@@ -60,7 +61,8 @@ interface ReplicationSchedulingContext {
function isCapabilityUnavailable(result: ReplicationOutcome): boolean {
return (
result.status === "blocked" &&
(result.reason === "capability-not-applicable" || result.reason === "capability-not-implemented")
(result.reason === CAPABILITY_UNAVAILABLE_REASONS.NOT_APPLICABLE ||
result.reason === CAPABILITY_UNAVAILABLE_REASONS.NOT_IMPLEMENTED)
);
}