diff --git a/plugin/package.json b/plugin/package.json index 0ca90363..acafe9da 100644 --- a/plugin/package.json +++ b/plugin/package.json @@ -16,8 +16,8 @@ "license": "MIT", "prettier": { "trailingComma": "none", - "tabWidth": 2, - "useTabs": false, + "tabWidth": 4, + "useTabs": true, "endOfLine": "lf" }, "devDependencies": { diff --git a/plugin/src/database/database.ts b/plugin/src/database/database.ts index 6e548d9c..8e56fd1e 100644 --- a/plugin/src/database/database.ts +++ b/plugin/src/database/database.ts @@ -4,7 +4,7 @@ import type { DocumentId, DocumentMetadata, RelativePath, - VaultUpdateId, + VaultUpdateId } from "./document-metadata"; import { Logger } from "src/tracing/logger"; @@ -47,7 +47,7 @@ export class Database { this._settings = { ...DEFAULT_SETTINGS, - ...(initialState.settings ?? {}), + ...(initialState.settings ?? {}) }; Logger.getInstance().debug( @@ -128,7 +128,7 @@ export class Database { documentId, relativePath, parentVersionId, - hash, + hash }: { documentId: DocumentId; relativePath: RelativePath; @@ -138,7 +138,7 @@ export class Database { this._documents.set(relativePath, { documentId, parentVersionId, - hash, + hash }); await this.save(); } @@ -148,7 +148,7 @@ export class Database { oldRelativePath, relativePath, parentVersionId, - hash, + hash }: { documentId: DocumentId; oldRelativePath: RelativePath; @@ -160,7 +160,7 @@ export class Database { this._documents.set(relativePath, { documentId, parentVersionId, - hash, + hash }); await this.save(); } @@ -180,7 +180,7 @@ export class Database { await this.saveData({ documents: Object.fromEntries(this._documents.entries()), settings: this._settings, - lastSeenUpdateId: this._lastSeenUpdateId, + lastSeenUpdateId: this._lastSeenUpdateId }); } } diff --git a/plugin/src/database/sync-settings.ts b/plugin/src/database/sync-settings.ts index fcf7f88c..ad6aa5cd 100644 --- a/plugin/src/database/sync-settings.ts +++ b/plugin/src/database/sync-settings.ts @@ -19,5 +19,5 @@ export const DEFAULT_SETTINGS: SyncSettings = { syncConcurrency: 1, isSyncEnabled: false, displayNoopSyncEvents: false, - minimumLogLevel: LogLevel.INFO, + minimumLogLevel: LogLevel.INFO }; diff --git a/plugin/src/events/obisidan-event-handler.ts b/plugin/src/events/obisidan-event-handler.ts index 200ed8a0..2eae62ff 100644 --- a/plugin/src/events/obisidan-event-handler.ts +++ b/plugin/src/events/obisidan-event-handler.ts @@ -39,7 +39,7 @@ export class ObsidianFileEventHandler implements FileEventHandler { await this.syncer.syncLocallyUpdatedFile({ oldPath, relativePath: file.path, - updateTime: new Date(file.stat.ctime), + updateTime: new Date(file.stat.ctime) }); } else { Logger.getInstance().debug( @@ -54,7 +54,7 @@ export class ObsidianFileEventHandler implements FileEventHandler { await this.syncer.syncLocallyUpdatedFile({ relativePath: file.path, - updateTime: new Date(file.stat.ctime), + updateTime: new Date(file.stat.ctime) }); } else { Logger.getInstance().debug( diff --git a/plugin/src/file-operations/obsidian-file-operations.ts b/plugin/src/file-operations/obsidian-file-operations.ts index ef2d45e8..c578018f 100644 --- a/plugin/src/file-operations/obsidian-file-operations.ts +++ b/plugin/src/file-operations/obsidian-file-operations.ts @@ -1,8 +1,8 @@ import type { Vault } from "obsidian"; import { normalizePath } from "obsidian"; import type { FileOperations } from "./file-operations"; -import * as lib from "../../../backend/sync_lib/pkg/sync_lib.js"; import type { RelativePath } from "src/database/document-metadata"; +import { isBinary, mergeText } from "sync_lib"; export class ObsidianFileOperations implements FileOperations { public constructor(private readonly vault: Vault) {} @@ -49,7 +49,7 @@ export class ObsidianFileOperations implements FileOperations { return new Uint8Array(0); } - if (lib.isBinary(expectedContent) || !path.endsWith(".md")) { + if (isBinary(expectedContent) || !path.endsWith(".md")) { await this.vault.adapter.writeBinary( normalizePath(path), newContent @@ -64,7 +64,7 @@ export class ObsidianFileOperations implements FileOperations { normalizePath(path), (currentText) => { if (currentText !== expetedText) { - return lib.mergeText(expetedText, currentText, newText); + return mergeText(expetedText, currentText, newText); } return newText; diff --git a/plugin/src/services/sync-service.ts b/plugin/src/services/sync-service.ts index 3f3a94a7..32f09d8b 100644 --- a/plugin/src/services/sync-service.ts +++ b/plugin/src/services/sync-service.ts @@ -1,17 +1,16 @@ -import * as lib from "../../../backend/sync_lib/pkg/sync_lib.js"; - import type { Client } from "openapi-fetch"; import createClient from "openapi-fetch"; -import type { components, paths } from "./types.js"; // Generated by openapi-typescript +import type { components, paths } from "./types"; // Generated by openapi-typescript import type { Database } from "src/database/database"; import type { SyncSettings } from "src/database/sync-settings"; import type { DocumentId, RelativePath, - VaultUpdateId, + VaultUpdateId } from "src/database/document-metadata"; -import { Logger } from "src/tracing/logger.js"; -import { retriedFetch } from "src/utils/retried-fetch.js"; +import { Logger } from "src/tracing/logger"; +import { retriedFetch } from "src/utils/retried-fetch"; +import { bytesToBase64 } from "sync_lib"; export interface CheckConnectionResult { isSuccessful: boolean; @@ -45,11 +44,9 @@ export class SyncService { const response = await this.clientWithoutRetries.GET("/ping", { params: { header: { - authorization: `Bearer ${ - this.database.getSettings().token - }`, - }, - }, + authorization: `Bearer ${this.database.getSettings().token}` + } + } }); Logger.getInstance().debug( @@ -58,9 +55,7 @@ export class SyncService { if (!response.data) { throw new Error( - `Failed to ping server: ${SyncService.formatError( - response.error - )}` + `Failed to ping server: ${SyncService.formatError(response.error)}` ); } @@ -70,7 +65,7 @@ export class SyncService { public async create({ relativePath, contentBytes, - createdDate, + createdDate }: { relativePath: RelativePath; contentBytes: Uint8Array; @@ -81,27 +76,23 @@ export class SyncService { { params: { path: { - vault_id: this.database.getSettings().vaultName, + vault_id: this.database.getSettings().vaultName }, header: { - authorization: `Bearer ${ - this.database.getSettings().token - }`, - }, + authorization: `Bearer ${this.database.getSettings().token}` + } }, body: { - contentBase64: lib.bytesToBase64(contentBytes), + contentBase64: bytesToBase64(contentBytes), createdDate: createdDate.toISOString(), - relativePath, - }, + relativePath + } } ); if (!response.data) { throw new Error( - `Failed to create document: ${SyncService.formatError( - response.error - )}` + `Failed to create document: ${SyncService.formatError(response.error)}` ); } @@ -119,7 +110,7 @@ export class SyncService { documentId, relativePath, contentBytes, - createdDate, + createdDate }: { parentVersionId: VaultUpdateId; documentId: DocumentId; @@ -133,28 +124,24 @@ export class SyncService { params: { path: { vault_id: this.database.getSettings().vaultName, - document_id: documentId, + document_id: documentId }, header: { - authorization: `Bearer ${ - this.database.getSettings().token - }`, - }, + authorization: `Bearer ${this.database.getSettings().token}` + } }, body: { parentVersionId, - contentBase64: lib.bytesToBase64(contentBytes), + contentBase64: bytesToBase64(contentBytes), createdDate: createdDate.toISOString(), - relativePath, - }, + relativePath + } } ); if (!response.data) { throw new Error( - `Failed to update document: ${SyncService.formatError( - response.error - )}` + `Failed to update document: ${SyncService.formatError(response.error)}` ); } @@ -168,7 +155,7 @@ export class SyncService { public async delete({ documentId, relativePath, - createdDate, + createdDate }: { documentId: DocumentId; relativePath: RelativePath; @@ -180,18 +167,16 @@ export class SyncService { params: { path: { vault_id: this.database.getSettings().vaultName, - document_id: documentId, + document_id: documentId }, header: { - authorization: `Bearer ${ - this.database.getSettings().token - }`, - }, + authorization: `Bearer ${this.database.getSettings().token}` + } }, body: { createdDate: createdDate.toISOString(), - relativePath, - }, + relativePath + } } ); @@ -207,7 +192,7 @@ export class SyncService { } public async get({ - documentId, + documentId }: { documentId: DocumentId; }): Promise { @@ -217,22 +202,18 @@ export class SyncService { params: { path: { vault_id: this.database.getSettings().vaultName, - document_id: documentId, + document_id: documentId }, header: { - authorization: `Bearer ${ - this.database.getSettings().token - }`, - }, - }, + authorization: `Bearer ${this.database.getSettings().token}` + } + } } ); if (!response.data) { throw new Error( - `Failed to get document: ${SyncService.formatError( - response.error - )}` + `Failed to get document: ${SyncService.formatError(response.error)}` ); } @@ -249,25 +230,21 @@ export class SyncService { const response = await this.client.GET("/vaults/{vault_id}/documents", { params: { path: { - vault_id: this.database.getSettings().vaultName, + vault_id: this.database.getSettings().vaultName }, header: { - authorization: `Bearer ${ - this.database.getSettings().token - }`, + authorization: `Bearer ${this.database.getSettings().token}` }, query: { - since_update_id: since, - }, - }, + since_update_id: since + } + } }); const { error } = response; if (error) { throw new Error( - `Failed to get documents: ${SyncService.formatError( - response.error - )}` + `Failed to get documents: ${SyncService.formatError(response.error)}` ); } @@ -284,18 +261,18 @@ export class SyncService { if (result.isAuthenticated) { return { isSuccessful: true, - message: `Successfully connected to server (version: ${result.serverVersion}) and authenticated.`, + message: `Successfully connected to server (version: ${result.serverVersion}) and authenticated.` }; } return { isSuccessful: false, - message: `Successfully connected to server (version: ${result.serverVersion}) but failed to authenticate.`, + message: `Successfully connected to server (version: ${result.serverVersion}) but failed to authenticate.` }; } catch (e) { return { isSuccessful: false, - message: `Failed to connect to server: ${e}`, + message: `Failed to connect to server: ${e}` }; } } @@ -303,11 +280,11 @@ export class SyncService { private createClient(settings: SyncSettings): void { this.client = createClient({ baseUrl: settings.remoteUri, - fetch: retriedFetch, + fetch: retriedFetch }); this.clientWithoutRetries = createClient({ - baseUrl: settings.remoteUri, + baseUrl: settings.remoteUri }); } } diff --git a/plugin/src/services/types.ts b/plugin/src/services/types.ts index f8f748f1..4f837259 100644 --- a/plugin/src/services/types.ts +++ b/plugin/src/services/types.ts @@ -4,380 +4,382 @@ */ export interface paths { - "/ping": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: { - parameters: { - query?: never; - header?: { - authorization?: string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PingResponse"]; - }; - }; - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SerializedError"]; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/vaults/{vault_id}/documents": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: { - parameters: { - query?: { - since_update_id?: number | null; - }; - header: { - authorization: string; - }; - path: { - vault_id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["FetchLatestDocumentsResponse"]; - }; - }; - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SerializedError"]; - }; - }; - }; - }; - put?: never; - post: { - parameters: { - query?: never; - header: { - authorization: string; - }; - path: { - vault_id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["CreateDocumentVersion"]; - }; - }; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["DocumentUpdateResponse"]; - }; - }; - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SerializedError"]; - }; - }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/vaults/{vault_id}/documents/{document_id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: { - parameters: { - query?: never; - header: { - authorization: string; - }; - path: { - document_id: string; - vault_id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["DocumentVersion"]; - }; - }; - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SerializedError"]; - }; - }; - }; - }; - put: { - parameters: { - query?: never; - header: { - authorization: string; - }; - path: { - document_id: string; - vault_id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpdateDocumentVersion"]; - }; - }; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["DocumentUpdateResponse"]; - }; - }; - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SerializedError"]; - }; - }; - }; - }; - post?: never; - delete: { - parameters: { - query?: never; - header: { - authorization: string; - }; - path: { - document_id: string; - vault_id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["DeleteDocumentVersion"]; - }; - }; - responses: { - /** @description no content */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SerializedError"]; - }; - }; - }; - }; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; + "/ping": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: { + parameters: { + query?: never; + header?: { + authorization?: string; + }; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PingResponse"]; + }; + }; + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SerializedError"]; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/vaults/{vault_id}/documents": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: { + parameters: { + query?: { + since_update_id?: number | null; + }; + header: { + authorization: string; + }; + path: { + vault_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FetchLatestDocumentsResponse"]; + }; + }; + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SerializedError"]; + }; + }; + }; + }; + put?: never; + post: { + parameters: { + query?: never; + header: { + authorization: string; + }; + path: { + vault_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateDocumentVersion"]; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DocumentUpdateResponse"]; + }; + }; + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SerializedError"]; + }; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/vaults/{vault_id}/documents/{document_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: { + parameters: { + query?: never; + header: { + authorization: string; + }; + path: { + document_id: string; + vault_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DocumentVersion"]; + }; + }; + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SerializedError"]; + }; + }; + }; + }; + put: { + parameters: { + query?: never; + header: { + authorization: string; + }; + path: { + document_id: string; + vault_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UpdateDocumentVersion"]; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DocumentUpdateResponse"]; + }; + }; + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SerializedError"]; + }; + }; + }; + }; + post?: never; + delete: { + parameters: { + query?: never; + header: { + authorization: string; + }; + path: { + document_id: string; + vault_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["DeleteDocumentVersion"]; + }; + }; + responses: { + /** @description no content */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SerializedError"]; + }; + }; + }; + }; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; } export type webhooks = Record; export interface components { - schemas: { - CreateDocumentVersion: { - contentBase64: string; - /** Format: date-time */ - createdDate: string; - relativePath: string; - }; - DeleteDocumentVersion: { - /** Format: date-time */ - createdDate: string; - relativePath: string; - }; - /** @description Response to a create/update document request. */ - DocumentUpdateResponse: { - /** Format: date-time */ - createdDate: string; - /** Format: uuid */ - documentId: string; - isDeleted: boolean; - relativePath: string; - /** @enum {string} */ - type: "FastForwardUpdate"; - /** Format: date-time */ - updatedDate: string; - vaultId: string; - /** Format: int64 */ - vaultUpdateId: number; - } | { - contentBase64: string; - /** Format: date-time */ - createdDate: string; - /** Format: uuid */ - documentId: string; - isDeleted: boolean; - relativePath: string; - /** @enum {string} */ - type: "MergingUpdate"; - /** Format: date-time */ - updatedDate: string; - vaultId: string; - /** Format: int64 */ - vaultUpdateId: number; - }; - DocumentVersion: { - contentBase64: string; - /** Format: date-time */ - createdDate: string; - /** Format: uuid */ - documentId: string; - isDeleted: boolean; - relativePath: string; - /** Format: date-time */ - updatedDate: string; - vaultId: string; - /** Format: int64 */ - vaultUpdateId: number; - }; - DocumentVersionWithoutContent: { - /** Format: date-time */ - createdDate: string; - /** Format: uuid */ - documentId: string; - isDeleted: boolean; - relativePath: string; - /** Format: date-time */ - updatedDate: string; - vaultId: string; - /** Format: int64 */ - vaultUpdateId: number; - }; - /** @description Response to a fetch latest documents request. */ - FetchLatestDocumentsResponse: { - /** - * Format: int64 - * @description The update ID of the latest document in the response. - */ - lastUpdateId: number; - latestDocuments: components["schemas"]["DocumentVersionWithoutContent"][]; - }; - PathParams: { - vault_id: string; - }; - PathParams2: { - vault_id: string; - }; - PathParams3: { - /** Format: uuid */ - document_id: string; - vault_id: string; - }; - PathParams4: { - /** Format: uuid */ - document_id: string; - vault_id: string; - }; - PathParams5: { - /** Format: uuid */ - document_id: string; - vault_id: string; - }; - /** @description Response to a ping request. */ - PingResponse: { - /** @description Whether the client is authenticated based on the sent Authorization header. */ - isAuthenticated: boolean; - /** @description Semantic version of the server. */ - serverVersion: string; - }; - QueryParams: { - /** Format: int64 */ - since_update_id?: number | null; - }; - SerializedError: { - causes: string[]; - message: string; - }; - UpdateDocumentVersion: { - contentBase64: string; - /** Format: date-time */ - createdDate: string; - /** Format: int64 */ - parentVersionId: number; - relativePath: string; - }; - }; - responses: never; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; + schemas: { + CreateDocumentVersion: { + contentBase64: string; + /** Format: date-time */ + createdDate: string; + relativePath: string; + }; + DeleteDocumentVersion: { + /** Format: date-time */ + createdDate: string; + relativePath: string; + }; + /** @description Response to a create/update document request. */ + DocumentUpdateResponse: + | { + /** Format: date-time */ + createdDate: string; + /** Format: uuid */ + documentId: string; + isDeleted: boolean; + relativePath: string; + /** @enum {string} */ + type: "FastForwardUpdate"; + /** Format: date-time */ + updatedDate: string; + vaultId: string; + /** Format: int64 */ + vaultUpdateId: number; + } + | { + contentBase64: string; + /** Format: date-time */ + createdDate: string; + /** Format: uuid */ + documentId: string; + isDeleted: boolean; + relativePath: string; + /** @enum {string} */ + type: "MergingUpdate"; + /** Format: date-time */ + updatedDate: string; + vaultId: string; + /** Format: int64 */ + vaultUpdateId: number; + }; + DocumentVersion: { + contentBase64: string; + /** Format: date-time */ + createdDate: string; + /** Format: uuid */ + documentId: string; + isDeleted: boolean; + relativePath: string; + /** Format: date-time */ + updatedDate: string; + vaultId: string; + /** Format: int64 */ + vaultUpdateId: number; + }; + DocumentVersionWithoutContent: { + /** Format: date-time */ + createdDate: string; + /** Format: uuid */ + documentId: string; + isDeleted: boolean; + relativePath: string; + /** Format: date-time */ + updatedDate: string; + vaultId: string; + /** Format: int64 */ + vaultUpdateId: number; + }; + /** @description Response to a fetch latest documents request. */ + FetchLatestDocumentsResponse: { + /** + * Format: int64 + * @description The update ID of the latest document in the response. + */ + lastUpdateId: number; + latestDocuments: components["schemas"]["DocumentVersionWithoutContent"][]; + }; + PathParams: { + vault_id: string; + }; + PathParams2: { + vault_id: string; + }; + PathParams3: { + /** Format: uuid */ + document_id: string; + vault_id: string; + }; + PathParams4: { + /** Format: uuid */ + document_id: string; + vault_id: string; + }; + PathParams5: { + /** Format: uuid */ + document_id: string; + vault_id: string; + }; + /** @description Response to a ping request. */ + PingResponse: { + /** @description Whether the client is authenticated based on the sent Authorization header. */ + isAuthenticated: boolean; + /** @description Semantic version of the server. */ + serverVersion: string; + }; + QueryParams: { + /** Format: int64 */ + since_update_id?: number | null; + }; + SerializedError: { + causes: string[]; + message: string; + }; + UpdateDocumentVersion: { + contentBase64: string; + /** Format: date-time */ + createdDate: string; + /** Format: int64 */ + parentVersionId: number; + relativePath: string; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; } export type $defs = Record; export type operations = Record; diff --git a/plugin/src/sync-operations/apply-remote-changes-locally.ts b/plugin/src/sync-operations/apply-remote-changes-locally.ts index 0146463b..e5eb7208 100644 --- a/plugin/src/sync-operations/apply-remote-changes-locally.ts +++ b/plugin/src/sync-operations/apply-remote-changes-locally.ts @@ -8,7 +8,7 @@ let isRunning = false; export async function applyRemoteChangesLocally({ database, syncService, - syncer, + syncer }: { database: Database; syncService: SyncService; diff --git a/plugin/src/sync-operations/document-lock.test.ts b/plugin/src/sync-operations/document-lock.test.ts index 08afef97..1b79a225 100644 --- a/plugin/src/sync-operations/document-lock.test.ts +++ b/plugin/src/sync-operations/document-lock.test.ts @@ -1,7 +1,7 @@ import { tryLockDocument, waitForDocumentLock, - unlockDocument, + unlockDocument } from "./document-lock"; import type { RelativePath } from "src/database/document-metadata"; @@ -34,9 +34,9 @@ describe("Document Lock Operations", () => { }); test("should throw an error when unlocking a document that is not locked", () => { - expect(() => { unlockDocument(testPath); }).toThrow( - `Document ${testPath} is not locked, cannot unlock` - ); + expect(() => { + unlockDocument(testPath); + }).toThrow(`Document ${testPath} is not locked, cannot unlock`); }); test("should wait for a document lock and resolve when unlocked", async () => { diff --git a/plugin/src/sync-operations/syncer.ts b/plugin/src/sync-operations/syncer.ts index 15abec74..5621f7cd 100644 --- a/plugin/src/sync-operations/syncer.ts +++ b/plugin/src/sync-operations/syncer.ts @@ -1,10 +1,9 @@ import type { Database } from "src/database/database"; import type { DocumentMetadata, - RelativePath, + RelativePath } from "src/database/document-metadata"; import type { FileOperations } from "src/file-operations/file-operations"; -import * as lib from "../../../backend/sync_lib/pkg/sync_lib.js"; import type { SyncService } from "src/services/sync-service"; import { Logger } from "src/tracing/logger"; import type { SyncHistory } from "src/tracing/sync-history"; @@ -12,7 +11,8 @@ import { SyncSource, SyncStatus, SyncType } from "src/tracing/sync-history"; import { unlockDocument, waitForDocumentLock } from "./document-lock"; import PQueue from "p-queue"; import { EMPTY_HASH, hash } from "src/utils/hash"; -import type { components } from "src/services/types.js"; +import type { components } from "src/services/types"; +import { base64ToBytes } from "sync_lib"; export class Syncer { private readonly remainingOperationsListeners: (( @@ -30,7 +30,7 @@ export class Syncer { private readonly history: SyncHistory ) { this.syncQueue = new PQueue({ - concurrency: database.getSettings().syncConcurrency, + concurrency: database.getSettings().syncConcurrency }); database.addOnSettingsChangeHandlers((settings) => { @@ -78,9 +78,8 @@ export class Syncer { public async syncRemotelyUpdatedFile( remoteVersion: components["schemas"]["DocumentVersionWithoutContent"] ): Promise { - await this.syncQueue.add( - async () => - this.internalSyncRemotelyUpdatedFile(remoteVersion) + await this.syncQueue.add(async () => + this.internalSyncRemotelyUpdatedFile(remoteVersion) ); } @@ -104,7 +103,7 @@ export class Syncer { try { const allLocalFiles = await this.operations.listAllFiles(); const locallyDeletedFiles = [ - ...this.database.getDocuments().entries(), + ...this.database.getDocuments().entries() ].filter(([path, _]) => !allLocalFiles.includes(path)); await Promise.all( @@ -134,7 +133,7 @@ export class Syncer { updateTime: await this.operations.getModificationTime( relativePath - ), + ) }); } @@ -157,7 +156,7 @@ export class Syncer { updateTime: await this.operations.getModificationTime( relativePath - ), + ) }); }) ) @@ -218,7 +217,7 @@ export class Syncer { status: SyncStatus.NO_OP, relativePath, message: `File hash matches with last synced version, no need to sync`, - type: SyncType.UPDATE, + type: SyncType.UPDATE }); return; } @@ -227,7 +226,7 @@ export class Syncer { const response = await this.syncService.create({ relativePath, contentBytes, - createdDate: updateTime, + createdDate: updateTime }); this.history.addHistoryEntry({ @@ -235,13 +234,11 @@ export class Syncer { source: SyncSource.PUSH, relativePath, message: `Successfully uploaded locally created file`, - type: SyncType.CREATE, + type: SyncType.CREATE }); if (response.type === "MergingUpdate") { - const responseBytes = lib.base64ToBytes( - response.contentBase64 - ); + const responseBytes = base64ToBytes(response.contentBase64); contentHash = hash(responseBytes); await this.operations.write( @@ -254,7 +251,7 @@ export class Syncer { source: SyncSource.PULL, relativePath, message: `The file we created locally has already existed remotely, so we have merged them`, - type: SyncType.UPDATE, + type: SyncType.UPDATE }); } @@ -262,7 +259,7 @@ export class Syncer { documentId: response.documentId, relativePath: response.relativePath, parentVersionId: response.vaultUpdateId, - hash: contentHash, + hash: contentHash }); await this.tryIncrementVaultUpdateId(response.vaultUpdateId); @@ -273,7 +270,7 @@ export class Syncer { private async internalSyncLocallyUpdatedFile({ oldPath, relativePath, - updateTime, + updateTime }: { oldPath?: RelativePath; relativePath: RelativePath; @@ -293,7 +290,7 @@ export class Syncer { status: SyncStatus.NO_OP, relativePath, message: `The renaming doesn't require a sync because it must have been pulled from remote`, - type: SyncType.UPDATE, + type: SyncType.UPDATE }); return; } @@ -314,7 +311,7 @@ export class Syncer { status: SyncStatus.NO_OP, relativePath, message: `File hash matches with last synced version, no need to sync`, - type: SyncType.UPDATE, + type: SyncType.UPDATE }); return; } @@ -324,7 +321,7 @@ export class Syncer { parentVersionId: localMetadata.parentVersionId, relativePath, contentBytes, - createdDate: updateTime, + createdDate: updateTime }); this.history.addHistoryEntry({ @@ -332,7 +329,7 @@ export class Syncer { source: SyncSource.PUSH, relativePath, message: `Successfully uploaded locally updated file to the remote server`, - type: SyncType.UPDATE, + type: SyncType.UPDATE }); if (response.isDeleted) { @@ -348,7 +345,7 @@ export class Syncer { relativePath, message: "The file we tried to update had been deleted remotely, therefore, we have deleted it locally", - type: SyncType.DELETE, + type: SyncType.DELETE }); return; @@ -367,7 +364,7 @@ export class Syncer { } if (response.type === "MergingUpdate") { - const responseBytes = lib.base64ToBytes( + const responseBytes = base64ToBytes( response.contentBase64 ); contentHash = hash(responseBytes); @@ -383,7 +380,7 @@ export class Syncer { source: SyncSource.PULL, relativePath, message: `The file we updated had been updated remotely, so we downloaded the merged version`, - type: SyncType.UPDATE, + type: SyncType.UPDATE }); await this.database.moveDocument({ @@ -391,7 +388,7 @@ export class Syncer { oldRelativePath: oldPath ?? relativePath, relativePath: response.relativePath, parentVersionId: response.vaultUpdateId, - hash: contentHash, + hash: contentHash }); await this.tryIncrementVaultUpdateId( @@ -420,7 +417,7 @@ export class Syncer { status: SyncStatus.NO_OP, relativePath, message: `Locally deleted file hasn't been uploaded yet, so there's no need to delete it on the remote server`, - type: SyncType.DELETE, + type: SyncType.DELETE }); return; } @@ -428,7 +425,7 @@ export class Syncer { await this.syncService.delete({ documentId: localMetadata.documentId, relativePath, - createdDate: new Date(), // We got the event now, so it must have been deleted just now + createdDate: new Date() // We got the event now, so it must have been deleted just now }); this.history.addHistoryEntry({ @@ -436,7 +433,7 @@ export class Syncer { source: SyncSource.PUSH, relativePath, message: `Successfully deleted locally deleted file on the remote server`, - type: SyncType.DELETE, + type: SyncType.DELETE }); await this.database.removeDocument(relativePath); @@ -463,17 +460,17 @@ export class Syncer { source: SyncSource.PULL, relativePath: remoteVersion.relativePath, message: `Remotely deleted file hasn't been synced yet, so there's no need to delete it locally`, - type: SyncType.DELETE, + type: SyncType.DELETE }); return; } const content = ( await this.syncService.get({ - documentId: remoteVersion.documentId, + documentId: remoteVersion.documentId }) ).contentBase64; - const contentBytes = lib.base64ToBytes(content); + const contentBytes = base64ToBytes(content); await this.operations.create( remoteVersion.relativePath, @@ -483,14 +480,14 @@ export class Syncer { documentId: remoteVersion.documentId, relativePath: remoteVersion.relativePath, parentVersionId: remoteVersion.vaultUpdateId, - hash: hash(contentBytes), + hash: hash(contentBytes) }); this.history.addHistoryEntry({ status: SyncStatus.SUCCESS, source: SyncSource.PULL, relativePath: remoteVersion.relativePath, message: `Successfully downloaded remote file which hasn't existed locally`, - type: SyncType.CREATE, + type: SyncType.CREATE }); return; } @@ -516,12 +513,11 @@ export class Syncer { source: SyncSource.PULL, relativePath: remoteVersion.relativePath, message: `Successfully deleted remotely deleted file locally`, - type: SyncType.DELETE, + type: SyncType.DELETE }); } else { - const currentContent = await this.operations.read( - relativePath - ); + const currentContent = + await this.operations.read(relativePath); const currentHash = hash(currentContent); if (currentHash !== metadata.hash) { @@ -533,10 +529,10 @@ export class Syncer { const content = ( await this.syncService.get({ - documentId: remoteVersion.documentId, + documentId: remoteVersion.documentId }) ).contentBase64; - const contentBytes = lib.base64ToBytes(content); + const contentBytes = base64ToBytes(content); const contentHash = hash(contentBytes); if (relativePath !== remoteVersion.relativePath) { @@ -556,7 +552,7 @@ export class Syncer { oldRelativePath: relativePath, relativePath: remoteVersion.relativePath, parentVersionId: remoteVersion.vaultUpdateId, - hash: contentHash, + hash: contentHash }); this.history.addHistoryEntry({ @@ -564,7 +560,7 @@ export class Syncer { source: SyncSource.PULL, relativePath: remoteVersion.relativePath, message: `Successfully updated remotely updated file locally`, - type: SyncType.UPDATE, + type: SyncType.UPDATE }); } } finally { @@ -599,7 +595,7 @@ export class Syncer { relativePath, message: `Failed to ${syncSource.toLocaleLowerCase()} file ${e} when trying to ${syncType.toLocaleLowerCase()} it`, type: syncType, - source: syncSource, + source: syncSource }); throw e; } finally { diff --git a/plugin/src/tracing/logger.ts b/plugin/src/tracing/logger.ts index fdd86622..b1d19ba9 100644 --- a/plugin/src/tracing/logger.ts +++ b/plugin/src/tracing/logger.ts @@ -4,19 +4,22 @@ export enum LogLevel { DEBUG = "DEBUG", INFO = "INFO", WARNING = "WARNING", - ERROR = "ERROR", + ERROR = "ERROR" } const LOG_LEVEL_ORDER = { [LogLevel.DEBUG]: 0, [LogLevel.INFO]: 1, [LogLevel.WARNING]: 2, - [LogLevel.ERROR]: 3, + [LogLevel.ERROR]: 3 }; class LogLine { public timestamp = new Date(); - public constructor(public level: LogLevel, public message: string) {} + public constructor( + public level: LogLevel, + public message: string + ) {} } export class Logger { diff --git a/plugin/src/tracing/sync-history.ts b/plugin/src/tracing/sync-history.ts index aca45668..5bdda797 100644 --- a/plugin/src/tracing/sync-history.ts +++ b/plugin/src/tracing/sync-history.ts @@ -12,18 +12,18 @@ export interface CommonHistoryEntry { export enum SyncType { CREATE = "CREATE", UPDATE = "UPDATE", - DELETE = "DELETE", + DELETE = "DELETE" } export enum SyncSource { PUSH = "PUSH", - PULL = "PULL", + PULL = "PULL" } export enum SyncStatus { NO_OP = "NO_OP", SUCCESS = "SUCCESS", - ERROR = "ERROR", + ERROR = "ERROR" } export type HistoryEntry = CommonHistoryEntry & { timestamp: Date }; @@ -44,7 +44,7 @@ export class SyncHistory { private status: HistoryStats = { success: 0, - error: 0, + error: 0 }; public getEntries(): HistoryEntry[] { @@ -55,7 +55,7 @@ export class SyncHistory { this.entries.length = 0; this.status = { success: 0, - error: 0, + error: 0 }; this.syncHistoryUpdateListeners.forEach((listener) => { listener(this.status); @@ -72,7 +72,7 @@ export class SyncHistory { public addHistoryEntry(entry: CommonHistoryEntry): void { const historyEntry = { ...entry, - timestamp: new Date(), + timestamp: new Date() }; this.entries.push(historyEntry); diff --git a/plugin/src/utils/retried-fetch.ts b/plugin/src/utils/retried-fetch.ts index 484b8bea..d6694ae7 100644 --- a/plugin/src/utils/retried-fetch.ts +++ b/plugin/src/utils/retried-fetch.ts @@ -22,9 +22,7 @@ export async function retriedFetch( retryOn: function (attempt, error, response) { if (error !== null || !response || response.status >= 500) { Logger.getInstance().warn( - `Retrying fetch for ${getUrlFromInput( - input - )}, attempt ${attempt}` + `Retrying fetch for ${getUrlFromInput(input)}, attempt ${attempt}` ); return true; @@ -33,6 +31,6 @@ export async function retriedFetch( }, retries: 6, retryDelay: (attempt) => Math.pow(1.5, attempt) * 500, - ...init, + ...init }); } diff --git a/plugin/src/vault-link-plugin.ts b/plugin/src/vault-link-plugin.ts index 5460498c..118393aa 100644 --- a/plugin/src/vault-link-plugin.ts +++ b/plugin/src/vault-link-plugin.ts @@ -1,22 +1,22 @@ import type { WorkspaceLeaf } from "obsidian"; import { Plugin } from "obsidian"; - -import * as lib from "../../backend/sync_lib/pkg/sync_lib.js"; -import * as wasmBin from "../../backend/sync_lib/pkg/sync_lib_bg.wasm"; -import { SyncSettingsTab } from "./views/settings-tab.js"; -import { HistoryView } from "./views/history-view.js"; - -import { ObsidianFileEventHandler } from "./events/obisidan-event-handler.js"; -import { SyncService } from "./services/sync-service.js"; -import { Database } from "./database/database.js"; -import { applyRemoteChangesLocally } from "./sync-operations/apply-remote-changes-locally.js"; -import { ObsidianFileOperations } from "./file-operations/obsidian-file-operations.js"; -import { StatusBar } from "./views/status-bar.js"; -import { Logger } from "./tracing/logger.js"; -import { SyncHistory } from "./tracing/sync-history.js"; -import { LogsView } from "./views/logs-view.js"; -import { Syncer } from "./sync-operations/syncer.js"; -import { StatusDescription } from "./views/status-description.js"; +import "./styles.scss"; +import "../manifest.json"; +import init, { setPanicHook } from "sync_lib"; +import wasmBin from "sync_lib/sync_lib_bg.wasm"; +import { SyncSettingsTab } from "./views/settings-tab"; +import { HistoryView } from "./views/history-view"; +import { ObsidianFileEventHandler } from "./events/obisidan-event-handler"; +import { SyncService } from "./services/sync-service"; +import { Database } from "./database/database"; +import { applyRemoteChangesLocally } from "./sync-operations/apply-remote-changes-locally"; +import { ObsidianFileOperations } from "./file-operations/obsidian-file-operations"; +import { StatusBar } from "./views/status-bar"; +import { Logger } from "./tracing/logger"; +import { SyncHistory } from "./tracing/sync-history"; +import { LogsView } from "./views/logs-view"; +import { Syncer } from "./sync-operations/syncer"; +import { StatusDescription } from "./views/status-description"; export default class VaultLinkPlugin extends Plugin { private readonly operations = new ObsidianFileOperations(this.app.vault); @@ -27,14 +27,10 @@ export default class VaultLinkPlugin extends Plugin { public async onload(): Promise { Logger.getInstance().info("Starting plugin"); - await lib.default( - Promise.resolve( - // eslint-disable-next-line - (wasmBin as any).default - ) - ); + // eslint-disable-next-line + await init((wasmBin as any).default); - lib.setPanicHook(); + setPanicHook(); const database = new Database( await this.loadData(), @@ -63,7 +59,7 @@ export default class VaultLinkPlugin extends Plugin { database, syncService, statusDescription, - syncer, + syncer }); this.addSettingTab(this.settingsTab); @@ -90,7 +86,7 @@ export default class VaultLinkPlugin extends Plugin { this.app.vault.on( "rename", eventHandler.onRename.bind(eventHandler) - ), + ) ].forEach((event) => { this.registerEvent(event); }); @@ -196,7 +192,7 @@ export default class VaultLinkPlugin extends Plugin { applyRemoteChangesLocally({ database, syncService, - syncer, + syncer }), intervalMs ); diff --git a/plugin/src/views/history-view.ts b/plugin/src/views/history-view.ts index 1ce3036e..ca2f248e 100644 --- a/plugin/src/views/history-view.ts +++ b/plugin/src/views/history-view.ts @@ -61,7 +61,7 @@ export class HistoryView extends ItemView { } element.createEl("span", { - text: entry.relativePath, + text: entry.relativePath }); const syncSourceIcon = HistoryView.getSyncSourceIcon(entry.source); @@ -107,7 +107,7 @@ export class HistoryView extends ItemView { entries.forEach((entry) => { container.createDiv( { - cls: ["history-card", entry.status.toLocaleLowerCase()], + cls: ["history-card", entry.status.toLocaleLowerCase()] }, (card) => { if ( @@ -127,13 +127,13 @@ export class HistoryView extends ItemView { card.createDiv( { - cls: "history-card-header", + cls: "history-card-header" }, (header) => { header.createEl( "h5", { - cls: "history-card-title", + cls: "history-card-title" }, (title) => { HistoryView.renderSyncItemTitle( @@ -148,14 +148,14 @@ export class HistoryView extends ItemView { entry.timestamp, new Date() ), - cls: "history-card-timestamp", + cls: "history-card-timestamp" }); } ); card.createEl("p", { text: `${entry.message}.`, - cls: "history-card-message", + cls: "history-card-message" }); } ); diff --git a/plugin/src/views/logs-view.ts b/plugin/src/views/logs-view.ts index 4d36fa65..6880dc7b 100644 --- a/plugin/src/views/logs-view.ts +++ b/plugin/src/views/logs-view.ts @@ -61,13 +61,13 @@ export class LogsView extends ItemView { container.createEl( "p", { - text: "This view displays logs generated by VaultLink. You can set the log level in the ", + text: "This view displays logs generated by VaultLink. You can set the log level in the " }, (p) => { p.createEl( "a", { - text: "settings", + text: "settings" }, (button) => { button.addEventListener("click", () => { @@ -95,17 +95,17 @@ export class LogsView extends ItemView { logs.forEach((message) => element.createDiv( { - cls: ["log-message", message.level], + cls: ["log-message", message.level] }, (messageContainer) => { messageContainer.createEl("span", { text: LogsView.formatTimestamp( message.timestamp ), - cls: "timestamp", + cls: "timestamp" }); messageContainer.createEl("span", { - text: message.message, + text: message.message }); } ) diff --git a/plugin/src/views/settings-tab.ts b/plugin/src/views/settings-tab.ts index 74267642..0816ed6e 100644 --- a/plugin/src/views/settings-tab.ts +++ b/plugin/src/views/settings-tab.ts @@ -4,11 +4,11 @@ import { Notice, PluginSettingTab, Setting } from "obsidian"; import type VaultLinkPlugin from "src/vault-link-plugin"; import type { Database } from "src/database/database"; import type { SyncService } from "src/services/sync-service"; -import { Logger, LogLevel } from "src/tracing/logger"; import type { Syncer } from "src/sync-operations/syncer"; import type { StatusDescription } from "./status-description"; import { LogsView } from "./logs-view"; import { HistoryView } from "./history-view"; +import { Logger, LogLevel } from "src/tracing/logger"; export class SyncSettingsTab extends PluginSettingTab { private editedVaultName: string; @@ -26,7 +26,7 @@ export class SyncSettingsTab extends PluginSettingTab { database, syncService, statusDescription, - syncer, + syncer }: { app: App; plugin: VaultLinkPlugin; @@ -72,12 +72,12 @@ export class SyncSettingsTab extends PluginSettingTab { private renderSettingsHeader(containerEl: HTMLElement): void { containerEl.createEl("h2", { text: "VaultLink" }).createSpan({ text: this.plugin.manifest.version, - cls: "version", + cls: "version" }); containerEl.createDiv( { - cls: "description", + cls: "description" }, (descriptionContainer) => { this.setStatusDescriptionSubscription((): void => { @@ -90,13 +90,13 @@ export class SyncSettingsTab extends PluginSettingTab { containerEl.createDiv( { - cls: "button-container", + cls: "button-container" }, (buttonContainer) => { buttonContainer.createEl( "button", { - text: "Show history", + text: "Show history" }, (button) => (button.onclick = async (): Promise => { @@ -108,7 +108,7 @@ export class SyncSettingsTab extends PluginSettingTab { buttonContainer.createEl( "button", { - text: "Show logs", + text: "Show logs" }, (button) => (button.onclick = async (): Promise => { @@ -296,7 +296,7 @@ export class SyncSettingsTab extends PluginSettingTab { [LogLevel.DEBUG]: LogLevel.DEBUG, [LogLevel.INFO]: LogLevel.INFO, [LogLevel.WARNING]: LogLevel.WARNING, - [LogLevel.ERROR]: LogLevel.ERROR, + [LogLevel.ERROR]: LogLevel.ERROR }) .onChange(async (value) => this.database.setSetting( diff --git a/plugin/src/views/status-bar.ts b/plugin/src/views/status-bar.ts index 6c3f43b4..0a5e8e1c 100644 --- a/plugin/src/views/status-bar.ts +++ b/plugin/src/views/status-bar.ts @@ -34,7 +34,7 @@ export class StatusBar { private updateStatus(): void { this.statusBarItem.empty(); const container = this.statusBarItem.createDiv({ - cls: ["sync-status"], + cls: ["sync-status"] }); let hasShownMessage = false; @@ -47,14 +47,14 @@ export class StatusBar { if ((this.lastHistoryStats?.success ?? 0) > 0) { hasShownMessage = true; container.createSpan({ - text: `${this.lastHistoryStats?.success ?? 0} ✅`, + text: `${this.lastHistoryStats?.success ?? 0} ✅` }); } if ((this.lastHistoryStats?.error ?? 0) > 0) { hasShownMessage = true; container.createSpan({ - text: `${this.lastHistoryStats?.error ?? 0} ❌`, + text: `${this.lastHistoryStats?.error ?? 0} ❌` }); } @@ -64,7 +64,7 @@ export class StatusBar { } else { const button = container.createEl("button", { text: "VaultLink is disabled, click to configure", - cls: "initialize-button", + cls: "initialize-button" }); button.onclick = (): void => { this.plugin.openSettings(); diff --git a/plugin/src/views/status-description.ts b/plugin/src/views/status-description.ts index 1b2f5362..fa7622f8 100644 --- a/plugin/src/views/status-description.ts +++ b/plugin/src/views/status-description.ts @@ -1,7 +1,7 @@ import type { Database } from "src/database/database"; import type { CheckConnectionResult, - SyncService, + SyncService } from "src/services/sync-service"; import type { Syncer } from "src/sync-operations/syncer"; import type { HistoryStats, SyncHistory } from "src/tracing/sync-history"; @@ -57,7 +57,7 @@ export class StatusDescription { if (this.lastConnectionState == undefined) { container.createSpan({ text: "VaultLink is starting up…", - cls: "warning", + cls: "warning" }); return; } @@ -65,7 +65,7 @@ export class StatusDescription { if (!this.lastConnectionState.isSuccessful) { container.createSpan({ text: `VaultLink failed to connect to the remote server with the error "${this.lastConnectionState.message}"`, - cls: "error", + cls: "error" }); return; } @@ -73,18 +73,18 @@ export class StatusDescription { container.createSpan({ text: "VaultLink is connected to the server " }); container.createEl("a", { text: this.database.getSettings().remoteUri, - href: this.database.getSettings().remoteUri, + href: this.database.getSettings().remoteUri }); container.createSpan({ - text: ` and has indexed approximately `, + text: ` and has indexed approximately ` }); container.createSpan({ text: `${this.database.getDocuments().size}`, - cls: "number", + cls: "number" }); container.createSpan({ - text: ` documents. `, + text: ` documents. ` }); if ( @@ -94,40 +94,40 @@ export class StatusDescription { ) { if (this.database.getSettings().isSyncEnabled) { container.createSpan({ - text: "Syncing is enabled but VaultLink hasn't found anything to sync yet.", + text: "Syncing is enabled but VaultLink hasn't found anything to sync yet." }); } else { container.createSpan({ text: "However, syncing is disabled right now.", - cls: "warning", + cls: "warning" }); } return; } container.createSpan({ - text: "The plugin has ", + text: "The plugin has " }); container.createSpan({ text: `${this.lastRemaining ?? 0}`, - cls: "number", + cls: "number" }); container.createSpan({ - text: " outstanding operations while having succeeded ", + text: " outstanding operations while having succeeded " }); container.createSpan({ text: `${this.lastHistoryStats?.success ?? 0}`, - cls: ["number", "good"], + cls: ["number", "good"] }); container.createSpan({ - text: " times and failed ", + text: " times and failed " }); container.createSpan({ text: `${this.lastHistoryStats?.error ?? 0}`, - cls: ["number", "bad"], + cls: ["number", "bad"] }); container.createSpan({ - text: " times.", + text: " times." }); }