From 9ad54eff7ab16daf1f037ee16677e6ad0c761714 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 15 Mar 2025 14:12:04 +0000 Subject: [PATCH] lint --- .../src/obisidan-event-handler.ts | 11 ++----- .../obsidian-plugin/src/views/history-view.ts | 2 +- .../obsidian-plugin/src/views/logs-view.ts | 2 +- .../obsidian-plugin/src/views/settings-tab.ts | 2 +- .../obsidian-plugin/src/views/status-bar.ts | 2 +- .../src/file-operations/document-locks.ts | 3 ++ .../file-operations/file-operations.test.ts | 2 +- .../src/file-operations/file-operations.ts | 4 +-- .../file-operations/filesystem-operations.ts | 2 +- .../safe-filesystem-operations.ts | 4 +-- .../sync-client/src/persistence/database.ts | 6 ++-- .../sync-client/src/persistence/settings.ts | 4 +-- .../src/services/connected-state.ts | 4 +-- .../sync-client/src/services/sync-service.ts | 2 +- .../sync-client/src/sync-operations/syncer.ts | 32 +++++++++---------- .../sync-operations/unrestricted-syncer.ts | 27 ++++++++-------- .../sync-client/src/tracing/sync-history.ts | 2 +- .../sync-client/src/utils/retried-fetch.ts | 2 +- frontend/sync-client/tsconfig.json | 15 ++++++--- frontend/test-client/src/agent/mock-client.ts | 18 +++++------ frontend/test-client/src/cli.ts | 5 ++- 21 files changed, 76 insertions(+), 75 deletions(-) diff --git a/frontend/obsidian-plugin/src/obisidan-event-handler.ts b/frontend/obsidian-plugin/src/obisidan-event-handler.ts index b2d58b70..9fb5dba4 100644 --- a/frontend/obsidian-plugin/src/obisidan-event-handler.ts +++ b/frontend/obsidian-plugin/src/obisidan-event-handler.ts @@ -9,10 +9,7 @@ export class ObsidianFileEventHandler { if (file instanceof TFile) { this.client.logger.info(`File created: ${file.path}`); - await this.client.syncer.syncLocallyCreatedFile( - file.path, - new Date(file.stat.ctime) - ); + await this.client.syncer.syncLocallyCreatedFile(file.path); } else { this.client.logger.debug(`Folder created: ${file.path}, ignored`); } @@ -34,8 +31,7 @@ export class ObsidianFileEventHandler { await this.client.syncer.syncLocallyUpdatedFile({ oldPath, - relativePath: file.path, - updateTime: new Date(file.stat.ctime) + relativePath: file.path }); } else { this.client.logger.debug( @@ -53,8 +49,7 @@ export class ObsidianFileEventHandler { this.client.logger.info(`File modified: ${file.path}`); await this.client.syncer.syncLocallyUpdatedFile({ - relativePath: file.path, - updateTime: new Date(file.stat.ctime) + relativePath: file.path }); } else { this.client.logger.debug(`Folder modified: ${file.path}, ignored`); diff --git a/frontend/obsidian-plugin/src/views/history-view.ts b/frontend/obsidian-plugin/src/views/history-view.ts index 56c8d539..ba2e18bc 100644 --- a/frontend/obsidian-plugin/src/views/history-view.ts +++ b/frontend/obsidian-plugin/src/views/history-view.ts @@ -60,7 +60,7 @@ export class HistoryView extends ItemView { } element.createEl("span", { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + text: entry.relativePath }); diff --git a/frontend/obsidian-plugin/src/views/logs-view.ts b/frontend/obsidian-plugin/src/views/logs-view.ts index 66aa30c2..22def86f 100644 --- a/frontend/obsidian-plugin/src/views/logs-view.ts +++ b/frontend/obsidian-plugin/src/views/logs-view.ts @@ -1,6 +1,6 @@ import type { WorkspaceLeaf } from "obsidian"; import { ItemView } from "obsidian"; -import type VaultLinkPlugin from "src/vault-link-plugin"; +import type VaultLinkPlugin from "../vault-link-plugin"; import type { SyncClient } from "sync-client"; export class LogsView extends ItemView { diff --git a/frontend/obsidian-plugin/src/views/settings-tab.ts b/frontend/obsidian-plugin/src/views/settings-tab.ts index b3a39dd7..d37a26dc 100644 --- a/frontend/obsidian-plugin/src/views/settings-tab.ts +++ b/frontend/obsidian-plugin/src/views/settings-tab.ts @@ -1,7 +1,7 @@ import type { App } from "obsidian"; import { Notice, PluginSettingTab, Setting } from "obsidian"; -import type VaultLinkPlugin from "src/vault-link-plugin"; +import type VaultLinkPlugin from "../vault-link-plugin"; import type { StatusDescription } from "./status-description"; import { LogsView } from "./logs-view"; import { HistoryView } from "./history-view"; diff --git a/frontend/obsidian-plugin/src/views/status-bar.ts b/frontend/obsidian-plugin/src/views/status-bar.ts index 9ecd7b87..ba7d7339 100644 --- a/frontend/obsidian-plugin/src/views/status-bar.ts +++ b/frontend/obsidian-plugin/src/views/status-bar.ts @@ -1,5 +1,5 @@ import type { HistoryStats, SyncClient } from "sync-client"; -import type VaultLinkPlugin from "src/vault-link-plugin"; +import type VaultLinkPlugin from "../vault-link-plugin"; export class StatusBar { private readonly statusBarItem: HTMLElement; diff --git a/frontend/sync-client/src/file-operations/document-locks.ts b/frontend/sync-client/src/file-operations/document-locks.ts index 3dc7ec29..522ed02a 100644 --- a/frontend/sync-client/src/file-operations/document-locks.ts +++ b/frontend/sync-client/src/file-operations/document-locks.ts @@ -1,6 +1,9 @@ import type { Logger } from "../tracing/logger"; import type { RelativePath } from "../persistence/database"; +// Manages locks on documents to prevent concurrent modifications +// allowing the client's FileOperations implementation to be simpler. +// Locks are granted in a first-in-first-out order. export class DocumentLocks { private readonly locked = new Set(); private readonly waiters = new Map void)[]>(); diff --git a/frontend/sync-client/src/file-operations/file-operations.test.ts b/frontend/sync-client/src/file-operations/file-operations.test.ts index 26ae3267..e0514ae8 100644 --- a/frontend/sync-client/src/file-operations/file-operations.test.ts +++ b/frontend/sync-client/src/file-operations/file-operations.test.ts @@ -1,4 +1,3 @@ -import type { FileSystemOperations } from "sync-client"; import type { Database, DocumentRecord, @@ -7,6 +6,7 @@ import type { import { FileOperations } from "./file-operations"; import { Logger } from "../tracing/logger"; import { assertSetContainsExactly } from "../utils/assert-set-contains-exactly"; +import type { FileSystemOperations } from "./filesystem-operations"; describe("File operations", () => { class MockDatabase { diff --git a/frontend/sync-client/src/file-operations/file-operations.ts b/frontend/sync-client/src/file-operations/file-operations.ts index f74576d5..74448efa 100644 --- a/frontend/sync-client/src/file-operations/file-operations.ts +++ b/frontend/sync-client/src/file-operations/file-operations.ts @@ -1,6 +1,6 @@ -import type { Logger } from "src/tracing/logger"; +import type { Logger } from "../tracing/logger"; import type { FileSystemOperations } from "./filesystem-operations"; -import type { Database, RelativePath } from "src/persistence/database"; +import type { Database, RelativePath } from "../persistence/database"; import { isBinary, isFileTypeMergable, mergeText } from "sync_lib"; import { FileNotFoundError, diff --git a/frontend/sync-client/src/file-operations/filesystem-operations.ts b/frontend/sync-client/src/file-operations/filesystem-operations.ts index 7c51ec78..3cab9d2d 100644 --- a/frontend/sync-client/src/file-operations/filesystem-operations.ts +++ b/frontend/sync-client/src/file-operations/filesystem-operations.ts @@ -1,4 +1,4 @@ -import type { RelativePath } from "src/persistence/database"; +import type { RelativePath } from "../persistence/database"; export interface FileSystemOperations { listAllFiles: () => Promise; diff --git a/frontend/sync-client/src/file-operations/safe-filesystem-operations.ts b/frontend/sync-client/src/file-operations/safe-filesystem-operations.ts index a2f7d111..c13611ef 100644 --- a/frontend/sync-client/src/file-operations/safe-filesystem-operations.ts +++ b/frontend/sync-client/src/file-operations/safe-filesystem-operations.ts @@ -12,7 +12,8 @@ export class FileNotFoundError extends Error { // Decorate FileSystemOperations replacing errors with FileNotFoundError // if the accessed file doesn't exist. It also ensures that there's only -// ever a single request in-flight for any one file. +// ever a single request in-flight for any one file through the use of +// DocumentLocks. export class SafeFileSystemOperations implements FileSystemOperations { private readonly locks: DocumentLocks; @@ -24,7 +25,6 @@ export class SafeFileSystemOperations implements FileSystemOperations { } public async listAllFiles(): Promise { - this.logger.debug("Listing all files"); return this.fs.listAllFiles(); } diff --git a/frontend/sync-client/src/persistence/database.ts b/frontend/sync-client/src/persistence/database.ts index 2bcd77bc..42b8be86 100644 --- a/frontend/sync-client/src/persistence/database.ts +++ b/frontend/sync-client/src/persistence/database.ts @@ -216,7 +216,7 @@ export class Database { relativePath: RelativePath, promise: Promise ): Promise { - let entry = this.getLatestDocumentByRelativePath(relativePath); + const entry = this.getLatestDocumentByRelativePath(relativePath); if (entry === undefined) { throw new Error( @@ -238,7 +238,7 @@ export class Database { relativePath: RelativePath, promise: Promise ): void { - let previousEntry = this.getLatestDocumentByRelativePath(relativePath); + const previousEntry = this.getLatestDocumentByRelativePath(relativePath); const entry = { relativePath, @@ -300,7 +300,7 @@ export class Database { ({ identity }) => identity !== oldDocument.identity ); - let newDocument = this.getLatestDocumentByRelativePath(newRelativePath); + const newDocument = this.getLatestDocumentByRelativePath(newRelativePath); if (newDocument !== undefined && !newDocument.isDeleted) { throw new Error( `Document already exists at new location: ${newRelativePath}` diff --git a/frontend/sync-client/src/persistence/settings.ts b/frontend/sync-client/src/persistence/settings.ts index 29a77ff7..dbeb8c15 100644 --- a/frontend/sync-client/src/persistence/settings.ts +++ b/frontend/sync-client/src/persistence/settings.ts @@ -1,5 +1,5 @@ -import type { Logger } from "src/tracing/logger"; -import { LogLevel } from "src/tracing/logger"; +import type { Logger } from "../tracing/logger"; +import { LogLevel } from "../tracing/logger"; export interface SyncSettings { remoteUri: string; diff --git a/frontend/sync-client/src/services/connected-state.ts b/frontend/sync-client/src/services/connected-state.ts index e21ad005..7e3a28e7 100644 --- a/frontend/sync-client/src/services/connected-state.ts +++ b/frontend/sync-client/src/services/connected-state.ts @@ -1,5 +1,5 @@ -import { Settings } from "../persistence/settings"; -import { Logger } from "../tracing/logger"; +import type { Settings } from "../persistence/settings"; +import type { Logger } from "../tracing/logger"; import { createPromise } from "../utils/create-promise"; import { retriedFetchFactory } from "../utils/retried-fetch"; diff --git a/frontend/sync-client/src/services/sync-service.ts b/frontend/sync-client/src/services/sync-service.ts index d784aa0d..92455643 100644 --- a/frontend/sync-client/src/services/sync-service.ts +++ b/frontend/sync-client/src/services/sync-service.ts @@ -8,7 +8,7 @@ import type { } from "../persistence/database"; import type { Logger } from "../tracing/logger"; import type { Settings } from "../persistence/settings"; -import { ConnectedState } from "./connected-state"; +import type { ConnectedState } from "./connected-state"; export interface CheckConnectionResult { isSuccessful: boolean; diff --git a/frontend/sync-client/src/sync-operations/syncer.ts b/frontend/sync-client/src/sync-operations/syncer.ts index 7cb31aac..d30f8457 100644 --- a/frontend/sync-client/src/sync-operations/syncer.ts +++ b/frontend/sync-client/src/sync-operations/syncer.ts @@ -1,17 +1,17 @@ import type { Database, RelativePath } from "../persistence/database"; -import type { SyncService } from "src/services/sync-service"; -import type { Logger } from "src/tracing/logger"; -import type { SyncHistory } from "src/tracing/sync-history"; +import type { SyncService } from "../services/sync-service"; +import type { Logger } from "../tracing/logger"; +import type { SyncHistory } from "../tracing/sync-history"; import PQueue from "p-queue"; import { v4 as uuidv4 } from "uuid"; -import { hash } from "src/utils/hash"; -import type { components } from "src/services/types"; -import type { Settings } from "src/persistence/settings"; -import type { FileOperations } from "src/file-operations/file-operations"; -import { findMatchingFile } from "src/utils/find-matching-file"; +import { hash } from "../utils/hash"; +import type { components } from "../services/types"; +import type { Settings } from "../persistence/settings"; +import type { FileOperations } from "../file-operations/file-operations"; +import { findMatchingFile } from "../utils/find-matching-file"; import { UnrestrictedSyncer } from "./unrestricted-syncer"; -import { FileNotFoundError } from "src/file-operations/safe-filesystem-operations"; -import { createPromise } from "src/utils/create-promise"; +import { FileNotFoundError } from "../file-operations/safe-filesystem-operations"; +import { createPromise } from "../utils/create-promise"; export class Syncer { private readonly remainingOperationsListeners: (( @@ -45,9 +45,9 @@ export class Syncer { }); this.syncQueue.on("active", () => - this.remainingOperationsListeners.forEach((listener) => - listener(this.syncQueue.size) - ) + { this.remainingOperationsListeners.forEach((listener) => + { listener(this.syncQueue.size); } + ); } ); this.internalSyncer = new UnrestrictedSyncer( @@ -107,7 +107,7 @@ export class Syncer { ); try { - await this.syncQueue.add(() => + await this.syncQueue.add(async () => this.internalSyncer.unrestrictedSyncLocallyCreatedFile( proposedDocumentId, () => this.database.getDocumentByUpdatePromise(promise) @@ -261,7 +261,7 @@ export class Syncer { public async reset(): Promise { this.syncQueue.clear(); await this.syncQueue.onEmpty(); - this.remainingOperationsListeners.forEach((listener) => listener(0)); + this.remainingOperationsListeners.forEach((listener) => { listener(0); }); this.internalSyncer.reset(); } @@ -297,7 +297,7 @@ export class Syncer { private async syncRemotelyUpdatedFile( remoteVersion: components["schemas"]["DocumentVersionWithoutContent"] ): Promise { - let document = this.database.getDocumentByDocumentId( + const document = this.database.getDocumentByDocumentId( remoteVersion.documentId ); diff --git a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts index 80949615..1734e25b 100644 --- a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts +++ b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts @@ -5,18 +5,18 @@ import type { RelativePath } from "../persistence/database"; -import type { SyncService } from "src/services/sync-service"; -import { Logger } from "src/tracing/logger"; -import type { SyncHistory } from "src/tracing/sync-history"; -import { SyncSource, SyncStatus, SyncType } from "src/tracing/sync-history"; -import { EMPTY_HASH, hash } from "src/utils/hash"; -import type { components } from "src/services/types"; -import { deserialize } from "src/utils/deserialize"; -import type { Settings } from "src/persistence/settings"; -import type { FileOperations } from "src/file-operations/file-operations"; -import { FileNotFoundError } from "src/file-operations/safe-filesystem-operations"; +import type { SyncService } from "../services/sync-service"; +import type { Logger } from "../tracing/logger"; +import type { SyncHistory } from "../tracing/sync-history"; +import { SyncSource, SyncStatus, SyncType } from "../tracing/sync-history"; +import { EMPTY_HASH, hash } from "../utils/hash"; +import type { components } from "../services/types"; +import { deserialize } from "../utils/deserialize"; +import type { Settings } from "../persistence/settings"; +import type { FileOperations } from "../file-operations/file-operations"; +import { FileNotFoundError } from "../file-operations/safe-filesystem-operations"; import { DocumentLocks } from "../file-operations/document-locks"; -import { createPromise } from "src/utils/create-promise"; +import { createPromise } from "../utils/create-promise"; export class UnrestrictedSyncer { private readonly locks: DocumentLocks; @@ -289,7 +289,6 @@ export class UnrestrictedSyncer { let localMetadata = getLatestDocument(); if ( - localMetadata !== undefined && localMetadata?.metadata !== undefined ) { // If the file exists locally, let's pretend the user has updated it @@ -352,11 +351,11 @@ export class UnrestrictedSyncer { remoteVersion.relativePath, contentBytes, () => - this.database.getNewResolvedDocumentByRelativePath( + { this.database.getNewResolvedDocumentByRelativePath( remoteVersion.documentId, remoteVersion.relativePath, promise - ) + ); } ); const document = diff --git a/frontend/sync-client/src/tracing/sync-history.ts b/frontend/sync-client/src/tracing/sync-history.ts index ea87bcac..ec8841e9 100644 --- a/frontend/sync-client/src/tracing/sync-history.ts +++ b/frontend/sync-client/src/tracing/sync-history.ts @@ -1,4 +1,4 @@ -import type { RelativePath } from "src/persistence/database"; +import type { RelativePath } from "../persistence/database"; import type { Logger } from "./logger"; export interface CommonHistoryEntry { diff --git a/frontend/sync-client/src/utils/retried-fetch.ts b/frontend/sync-client/src/utils/retried-fetch.ts index 1a49c704..a3856f8d 100644 --- a/frontend/sync-client/src/utils/retried-fetch.ts +++ b/frontend/sync-client/src/utils/retried-fetch.ts @@ -1,6 +1,6 @@ import * as fetchRetryFactory from "fetch-retry"; import type { RequestInitRetryParams } from "fetch-retry"; -import type { Logger } from "src/tracing/logger"; +import type { Logger } from "../tracing/logger"; function getUrlFromInput(input: RequestInfo | URL): string { if (input instanceof URL) { diff --git a/frontend/sync-client/tsconfig.json b/frontend/sync-client/tsconfig.json index 6db72fcc..40647521 100644 --- a/frontend/sync-client/tsconfig.json +++ b/frontend/sync-client/tsconfig.json @@ -1,12 +1,17 @@ { "compilerOptions": { - "baseUrl": ".", "module": "ESNext", "target": "ESNext", "strict": true, - "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, - "lib": ["DOM", "ESNext"] + "moduleResolution": "bundler", + "lib": [ + "DOM", // to get "fetch" + ], + "declaration": true, + "declarationDir": "./dist/types" }, - "exclude": ["./dist"] -} + "exclude": [ + "./dist" + ] +} \ No newline at end of file diff --git a/frontend/test-client/src/agent/mock-client.ts b/frontend/test-client/src/agent/mock-client.ts index 6cbcca06..261d4323 100644 --- a/frontend/test-client/src/agent/mock-client.ts +++ b/frontend/test-client/src/agent/mock-client.ts @@ -1,10 +1,10 @@ import { assert } from "../utils/assert"; -import type { - RelativePath, - FileSystemOperations, - SyncSettings +import { + type RelativePath, + type FileSystemOperations, + type SyncSettings, + SyncClient } from "sync-client"; -import { SyncClient } from "sync-client"; export class MockClient implements FileSystemOperations { protected readonly localFiles = new Map(); @@ -24,8 +24,8 @@ export class MockClient implements FileSystemOperations { await Promise.all( Object.keys(this.initialSettings).map(async (key) => { return this.client.settings.setSetting( - key as keyof SyncSettings, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - this.initialSettings[key as keyof SyncSettings] // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + key as keyof SyncSettings, + this.initialSettings[key as keyof SyncSettings]! ); }) ); @@ -88,10 +88,10 @@ export class MockClient implements FileSystemOperations { const newParts = newContent.split(" ").map((part) => part.trim()); existingParts.forEach((part) => // all changes should be additive - assert( + { assert( newParts.includes(part), `Part ${part} not found in new content` - ) + ); } ); this.client.logger.info( diff --git a/frontend/test-client/src/cli.ts b/frontend/test-client/src/cli.ts index 8e6e711b..f714dadb 100644 --- a/frontend/test-client/src/cli.ts +++ b/frontend/test-client/src/cli.ts @@ -92,7 +92,7 @@ async function runTest({ async function runTests(): Promise { const agentCounts = [2, 8]; const jitterScaleInSeconds = [0.5, 0, 2]; - const concurrencies = [1]; + const concurrencies = [16, 1]; const iterations = [50, 200]; const doDeletes = [true, false]; @@ -101,7 +101,7 @@ async function runTests(): Promise { for (const jitter of jitterScaleInSeconds) { for (const iteration of iterations) { for (const deleteFiles of doDeletes) { - for (let i = 0; i < 10; i++) { + for (let i = 0; i < 20; i++) { await runTest({ agentCount, concurrency, @@ -110,7 +110,6 @@ async function runTests(): Promise { jitterScaleInSeconds: jitter }); } - return; } } }