Simplify API

This commit is contained in:
Andras Schmelczer 2025-02-20 22:22:20 +00:00
parent 010b3d61e9
commit eb1ad9921a
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
5 changed files with 203 additions and 53 deletions

View file

@ -1,19 +1,6 @@
export { applyRemoteChangesLocally } from "./sync-operations/apply-remote-changes-locally";
export {
Database,
type RelativePath,
type DocumentId,
type VaultUpdateId,
type DocumentMetadata
} from "./persistence/database";
export { Settings, type SyncSettings } from "./persistence/settings";
export {
SyncService,
type CheckConnectionResult
} from "./services/sync-service";
export { type CheckConnectionResult } from "./services/sync-service";
export { Syncer } from "./sync-operations/syncer";
@ -25,26 +12,17 @@ export {
type HistoryStats,
type HistoryEntry
} from "./tracing/sync-history";
export { Logger, LogLevel } from "./tracing/logger";
export { SyncClient } from "./sync-client";
export { type FileOperations } from "./file-operations";
import init from "sync_lib";
import wasmBin from "sync_lib/sync_lib_bg.wasm";
export { type RelativePath } from "./persistence/database";
export type { PersistenceProvider } from "./persistence/persistence";
export {
isFileTypeMergable,
mergeText,
bytesToBase64,
base64ToBytes,
merge,
isBinary
merge
} from "sync_lib";
export const initialize = async (): Promise<void> => {
await init(
// eslint-disable-next-line
(wasmBin as any).default // it is loaded as a base64 string by webpack
);
};

View file

@ -0,0 +1,4 @@
export interface PersistenceProvider {
load: () => Promise<unknown>;
save: (data: unknown) => Promise<void>;
}

View file

@ -1,6 +1,6 @@
import type { Client } from "openapi-fetch";
import createClient from "openapi-fetch";
import type { components, paths } from "./types"; // Generated by openapi-typescript
import type { components, paths } from "./types"; // generated by openapi-typescript
import type {
DocumentId,
RelativePath,
@ -16,8 +16,8 @@ export interface CheckConnectionResult {
message: string;
}
export class SyncService {
private client: Client<paths>;
private clientWithoutRetries: Client<paths>;
private client!: Client<paths>;
private clientWithoutRetries!: Client<paths>;
public constructor(private readonly settings: Settings) {
this.createClient(settings.getSettings());
@ -39,28 +39,6 @@ export class SyncService {
return result;
}
public async ping(): Promise<components["schemas"]["PingResponse"]> {
const response = await this.clientWithoutRetries.GET("/ping", {
params: {
header: {
authorization: `Bearer ${this.settings.getSettings().token}`
}
}
});
Logger.getInstance().debug(
`Ping response: ${JSON.stringify(response.data)}`
);
if (!response.data) {
throw new Error(
`Failed to ping server: ${SyncService.formatError(response.error)}`
);
}
return response.data;
}
public async create({
relativePath,
contentBytes,
@ -282,6 +260,28 @@ export class SyncService {
}
}
private async ping(): Promise<components["schemas"]["PingResponse"]> {
const response = await this.clientWithoutRetries.GET("/ping", {
params: {
header: {
authorization: `Bearer ${this.settings.getSettings().token}`
}
}
});
Logger.getInstance().debug(
`Ping response: ${JSON.stringify(response.data)}`
);
if (!response.data) {
throw new Error(
`Failed to ping server: ${SyncService.formatError(response.error)}`
);
}
return response.data;
}
private createClient(settings: SyncSettings): void {
this.client = createClient<paths>({
baseUrl: settings.remoteUri,

View file

@ -0,0 +1,168 @@
import init from "sync_lib";
import wasmBin from "sync_lib/sync_lib_bg.wasm";
import type { PersistenceProvider } from "./persistence/persistence";
import { SyncHistory } from "./tracing/sync-history";
import type { FileOperations } from "./file-operations";
import { Logger } from "./tracing/logger";
import { Database } from "./persistence/database";
import { Settings } from "./persistence/settings";
import type { CheckConnectionResult } from "./services/sync-service";
import { SyncService } from "./services/sync-service";
import { Syncer } from "./sync-operations/syncer";
import { applyRemoteChangesLocally } from "./sync-operations/apply-remote-changes-locally";
export class SyncClient {
private remoteListenerIntervalId: number | null = null;
private constructor(
private readonly _history: SyncHistory,
private readonly _settings: Settings,
private readonly _database: Database,
private readonly _syncer: Syncer,
private readonly _syncService: SyncService
) {}
public get history(): SyncHistory {
return this._history;
}
public get settings(): Settings {
return this._settings;
}
public get syncer(): Syncer {
return this._syncer;
}
public static async create(
operations: FileOperations,
persistence: PersistenceProvider
): Promise<SyncClient> {
const history = new SyncHistory();
Logger.getInstance().info("Starting SyncClient");
await init(
// eslint-disable-next-line
(wasmBin as any).default // it is loaded as a base64 string by webpack
);
let state: Partial<{
settings: any;
database: any;
}> = (await persistence.load()) ?? {
settings: undefined,
database: undefined
};
const database = new Database(
state.database,
async (data: unknown): Promise<void> => {
state = { ...state, database: data };
return persistence.save(state);
}
);
const settings = new Settings(
state.settings,
async (data: unknown): Promise<void> => {
state = { ...state, settings: data };
return persistence.save(state);
}
);
const syncService = new SyncService(settings);
const syncer = new Syncer(
database,
settings,
syncService,
operations,
history
);
const client = new SyncClient(
history,
settings,
database,
syncer,
syncService
);
void syncer.scheduleSyncForOfflineChanges();
client.registerRemoteEventListener(
settings,
database,
syncService,
syncer,
settings.getSettings().fetchChangesUpdateIntervalMs
);
settings.addOnSettingsChangeHandlers((newSettings, oldSettings) => {
client.registerRemoteEventListener(
settings,
database,
syncService,
syncer,
newSettings.fetchChangesUpdateIntervalMs
);
if (!oldSettings.isSyncEnabled && newSettings.isSyncEnabled) {
syncer
.scheduleSyncForOfflineChanges()
.catch((_error: unknown) => {
Logger.getInstance().error(
"Failed to schedule sync for offline changes"
);
});
}
});
Logger.getInstance().info("SyncClient loaded");
return client;
}
public get documentCount(): number {
return this._database.getDocuments().size;
}
public async checkConnection(): Promise<CheckConnectionResult> {
return this._syncService.checkConnection();
}
public async reset(): Promise<void> {
await this._syncer.reset();
this._history.reset();
Logger.getInstance().reset();
}
public onunload(): void {
if (this.remoteListenerIntervalId !== null) {
window.clearInterval(this.remoteListenerIntervalId);
}
}
private registerRemoteEventListener(
settings: Settings,
database: Database,
syncService: SyncService,
syncer: Syncer,
intervalMs: number
): void {
if (this.remoteListenerIntervalId !== null) {
window.clearInterval(this.remoteListenerIntervalId);
}
this.remoteListenerIntervalId = window.setInterval(
// eslint-disable-next-line @typescript-eslint/no-misused-promises
async () =>
applyRemoteChangesLocally({
settings,
database,
syncService,
syncer
}),
intervalMs
);
}
}

View file

@ -1,4 +1,4 @@
import { RelativePath } from "src/persistence/database";
import type { RelativePath } from "src/persistence/database";
import { Logger } from "./logger";
export interface CommonHistoryEntry {