Migrate LiveSync flows to provider-owned resources

This commit is contained in:
vorotamoroz
2026-08-28 17:54:04 +00:00
parent f7206b1a6e
commit db70b4c2b6
34 changed files with 1830 additions and 365 deletions
@@ -0,0 +1,77 @@
import type { RemoteDBSettings } from "@vrtmrz/livesync-commonlib/compat/common/types";
import type {
ConnectionProbeFactory,
RemoteConnectionProbe,
RemoteConnectionProbeOptions,
} from "@vrtmrz/livesync-commonlib/replication";
import {
LiveSyncCouchDBReplicator,
type LiveSyncCouchDBReplicatorEnv,
} from "@vrtmrz/livesync-commonlib/compat/replication/couchdb/LiveSyncReplicator";
import { LiveSyncJournalReplicator } from "@vrtmrz/livesync-commonlib/compat/replication/journal/LiveSyncJournalReplicator";
import type { LiveSyncJournalReplicatorEnv } from "@vrtmrz/livesync-commonlib/compat/replication/journal/LiveSyncJournalReplicatorEnv";
import { createReplicatorDisposer, snapshotRemoteSettings } from "./shared";
export type ConnectionResourceHost = LiveSyncCouchDBReplicatorEnv & LiveSyncJournalReplicatorEnv;
function createCouchDBConnectionProbe(
replicator: LiveSyncCouchDBReplicator,
snapshot: RemoteDBSettings
): RemoteConnectionProbe {
const dispose = createReplicatorDisposer(replicator);
return {
check: async (options: RemoteConnectionProbeOptions = {}) => {
const connection = await replicator.connectRemoteCouchDBWithSetting(
snapshot,
replicator.isMobile(),
options.createIfMissing ?? true,
false
);
if (typeof connection === "string") {
return { ok: false, reason: connection };
}
try {
return { ok: true };
} finally {
await connection.close();
}
},
getStatus: () => replicator.getRemoteStatus(snapshot),
dispose,
};
}
function createObjectStorageConnectionProbe(
replicator: LiveSyncJournalReplicator,
snapshot: RemoteDBSettings
): RemoteConnectionProbe {
const dispose = createReplicatorDisposer(replicator);
return {
check: async (options: RemoteConnectionProbeOptions = {}) => {
try {
const connected = await replicator.tryConnectRemote(snapshot, options.showResult ?? false);
return connected ? { ok: true } : { ok: false };
} catch (error) {
return { ok: false, reason: error };
}
},
getStatus: () => replicator.getRemoteStatus(snapshot),
dispose,
};
}
/** Build an unpublished CouchDB connection resource for one host. */
export function createCouchDBConnectionProbeFactory(host: ConnectionResourceHost): ConnectionProbeFactory {
return (setting) => {
const snapshot = snapshotRemoteSettings(setting);
return Promise.resolve(createCouchDBConnectionProbe(new LiveSyncCouchDBReplicator(host), snapshot));
};
}
/** Build an unpublished Object Storage connection resource for one host. */
export function createObjectStorageConnectionProbeFactory(host: ConnectionResourceHost): ConnectionProbeFactory {
return (setting) => {
const snapshot = snapshotRemoteSettings(setting);
return Promise.resolve(createObjectStorageConnectionProbe(new LiveSyncJournalReplicator(host), snapshot));
};
}
+16
View File
@@ -0,0 +1,16 @@
export {
createCouchDBConnectionProbeFactory,
createObjectStorageConnectionProbeFactory,
type ConnectionResourceHost,
} from "./connection";
export {
createCouchDBPreferredTweakProbeFactory,
createObjectStoragePreferredTweakProbeFactory,
type PreferredTweakResourceHost,
} from "./preferredTweak";
export {
createCouchDBSecuritySeedResourceFactory,
createObjectStorageSecuritySeedResourceFactory,
type SecuritySeedResourceHost,
} from "./securitySeed";
export { createCouchDBSynchronisationInformationResourceFactory } from "./synchronisationInformation";
@@ -0,0 +1,43 @@
import type { RemoteDBSettings, RemotePreferredTweakResult } from "@vrtmrz/livesync-commonlib/compat/common/types";
import type { PreferredTweakProbe, PreferredTweakProbeFactory } from "@vrtmrz/livesync-commonlib/replication";
import {
LiveSyncCouchDBReplicator,
type LiveSyncCouchDBReplicatorEnv,
} from "@vrtmrz/livesync-commonlib/compat/replication/couchdb/LiveSyncReplicator";
import { LiveSyncJournalReplicator } from "@vrtmrz/livesync-commonlib/compat/replication/journal/LiveSyncJournalReplicator";
import type { LiveSyncJournalReplicatorEnv } from "@vrtmrz/livesync-commonlib/compat/replication/journal/LiveSyncJournalReplicatorEnv";
import { createReplicatorDisposer, snapshotRemoteSettings, type ResourceReplicator } from "./shared";
export type PreferredTweakResourceHost = LiveSyncCouchDBReplicatorEnv & LiveSyncJournalReplicatorEnv;
interface PreferredTweakReplicator extends ResourceReplicator {
getRemotePreferredTweakValues(setting: RemoteDBSettings): Promise<RemotePreferredTweakResult>;
}
function createPreferredTweakProbe(
replicator: PreferredTweakReplicator,
snapshot: RemoteDBSettings
): PreferredTweakProbe {
return {
read: () => replicator.getRemotePreferredTweakValues(snapshot),
dispose: createReplicatorDisposer(replicator),
};
}
/** Build an unpublished CouchDB preferred-tweak resource for one host. */
export function createCouchDBPreferredTweakProbeFactory(host: PreferredTweakResourceHost): PreferredTweakProbeFactory {
return (setting) => {
const snapshot = snapshotRemoteSettings(setting);
return Promise.resolve(createPreferredTweakProbe(new LiveSyncCouchDBReplicator(host), snapshot));
};
}
/** Build an unpublished Object Storage preferred-tweak resource for one host. */
export function createObjectStoragePreferredTweakProbeFactory(
host: PreferredTweakResourceHost
): PreferredTweakProbeFactory {
return (setting) => {
const snapshot = snapshotRemoteSettings(setting);
return Promise.resolve(createPreferredTweakProbe(new LiveSyncJournalReplicator(host), snapshot));
};
}
@@ -0,0 +1,35 @@
import type { SecuritySeedResourceFactory } from "@vrtmrz/livesync-commonlib/replication";
import {
LiveSyncCouchDBReplicator,
type LiveSyncCouchDBReplicatorEnv,
} from "@vrtmrz/livesync-commonlib/compat/replication/couchdb/LiveSyncReplicator";
import { LiveSyncJournalReplicator } from "@vrtmrz/livesync-commonlib/compat/replication/journal/LiveSyncJournalReplicator";
import type { LiveSyncJournalReplicatorEnv } from "@vrtmrz/livesync-commonlib/compat/replication/journal/LiveSyncJournalReplicatorEnv";
import { createReplicatorDisposer, snapshotRemoteSettings } from "./shared";
export type SecuritySeedResourceHost = LiveSyncCouchDBReplicatorEnv & LiveSyncJournalReplicatorEnv;
function createSecuritySeedResourceFactory(
createReplicator: () => LiveSyncCouchDBReplicator | LiveSyncJournalReplicator
): SecuritySeedResourceFactory {
return (setting) => {
const snapshot = snapshotRemoteSettings(setting);
const replicator = createReplicator();
return Promise.resolve({
read: () => replicator.getReplicationPBKDF2Salt(snapshot),
dispose: createReplicatorDisposer(replicator),
});
};
}
/** Build an unpublished CouchDB Security Seed resource for one host. */
export function createCouchDBSecuritySeedResourceFactory(host: SecuritySeedResourceHost): SecuritySeedResourceFactory {
return createSecuritySeedResourceFactory(() => new LiveSyncCouchDBReplicator(host));
}
/** Build an unpublished Object Storage Security Seed resource for one host. */
export function createObjectStorageSecuritySeedResourceFactory(
host: SecuritySeedResourceHost
): SecuritySeedResourceFactory {
return createSecuritySeedResourceFactory(() => new LiveSyncJournalReplicator(host));
}
+21
View File
@@ -0,0 +1,21 @@
import type { RemoteDBSettings } from "@vrtmrz/livesync-commonlib/compat/common/types";
export interface ResourceReplicator {
closeReplication(): void | Promise<void>;
}
/** Create one idempotent asynchronous disposer for a private Replicator. */
export function createReplicatorDisposer(replicator: ResourceReplicator): () => Promise<void> {
let disposal: Promise<void> | undefined;
return () => {
if (disposal === undefined) {
disposal = Promise.resolve().then(() => replicator.closeReplication());
}
return disposal;
};
}
/** Fence a finite resource from later edits to its source settings object. */
export function snapshotRemoteSettings(setting: RemoteDBSettings): RemoteDBSettings {
return { ...setting };
}
@@ -0,0 +1,35 @@
import type { SynchronisationInformationResourceFactory } from "@vrtmrz/livesync-commonlib/replication";
import {
LiveSyncCouchDBReplicator,
type LiveSyncCouchDBReplicatorEnv,
} from "@vrtmrz/livesync-commonlib/compat/replication/couchdb/LiveSyncReplicator";
import { checkSyncInfo } from "@vrtmrz/livesync-commonlib/compat/pouchdb/negotiation";
import { createReplicatorDisposer, snapshotRemoteSettings } from "./shared";
/** Build an owned CouchDB synchronisation-information verifier for one host. */
export function createCouchDBSynchronisationInformationResourceFactory(
host: LiveSyncCouchDBReplicatorEnv
): SynchronisationInformationResourceFactory {
return (setting) => {
const snapshot = snapshotRemoteSettings(setting);
const replicator = new LiveSyncCouchDBReplicator(host);
return Promise.resolve({
check: async () => {
const connection = await replicator.connectRemoteCouchDBWithSetting(
snapshot,
replicator.isMobile(),
true
);
if (typeof connection === "string") {
return false;
}
try {
return await checkSyncInfo(connection.db);
} finally {
await connection.close();
}
},
dispose: createReplicatorDisposer(replicator),
});
};
}