From 826e391f87109f3326f9d1c48a67c9e7fa999d8b Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 15 Mar 2025 15:17:21 +0000 Subject: [PATCH] lint --- .../obsidian-plugin/src/views/history-view.ts | 1 - frontend/sync-client/package.json | 2 +- .../src/file-operations/file-operations.ts | 28 +- .../sync-client/src/persistence/database.ts | 19 +- .../src/services/connected-state.ts | 20 +- frontend/sync-client/src/services/types.ts | 1172 +++++++++-------- .../sync-client/src/sync-operations/syncer.ts | 14 +- .../sync-operations/unrestricted-syncer.ts | 27 +- frontend/sync-client/tsconfig.json | 8 +- frontend/test-client/src/agent/mock-client.ts | 15 +- 10 files changed, 652 insertions(+), 654 deletions(-) diff --git a/frontend/obsidian-plugin/src/views/history-view.ts b/frontend/obsidian-plugin/src/views/history-view.ts index ba2e18bc..d253b27f 100644 --- a/frontend/obsidian-plugin/src/views/history-view.ts +++ b/frontend/obsidian-plugin/src/views/history-view.ts @@ -60,7 +60,6 @@ export class HistoryView extends ItemView { } element.createEl("span", { - text: entry.relativePath }); diff --git a/frontend/sync-client/package.json b/frontend/sync-client/package.json index d4a2850e..778c5d35 100644 --- a/frontend/sync-client/package.json +++ b/frontend/sync-client/package.json @@ -33,4 +33,4 @@ "webpack-merge": "^6.0.1", "sync_lib": "file:../../backend/sync_lib/pkg" } -} \ No newline at end of file +} diff --git a/frontend/sync-client/src/file-operations/file-operations.ts b/frontend/sync-client/src/file-operations/file-operations.ts index 74448efa..de99bcb9 100644 --- a/frontend/sync-client/src/file-operations/file-operations.ts +++ b/frontend/sync-client/src/file-operations/file-operations.ts @@ -53,10 +53,14 @@ export class FileOperations { // All parent directories are created if they don't exist. public async create( path: RelativePath, - newContent: Uint8Array, - whatevs?: any + newContent: Uint8Array ): Promise { this.logger.debug(`Creating file: ${path}`); + + await this.fs.write(path, newContent); + } + + public async ensureClearPath(path: RelativePath): Promise { if (await this.fs.exists(path)) { const deconflictedPath = await this.deconflictPath(path); this.logger.debug( @@ -68,10 +72,6 @@ export class FileOperations { } else { await this.createParentDirectories(path); } - - whatevs?.(); - - await this.fs.write(path, newContent); } // Update the file at the given path. @@ -143,19 +143,7 @@ export class FileOperations { return; } - if (await this.fs.exists(newPath)) { - const deconflictedPath = await this.deconflictPath(newPath); - this.logger.debug( - `Conflict when moving '${oldPath}' to '${newPath}', the latter already exists, deconflicting by moving it to '${deconflictedPath}'` - ); - - // this.database.move(newPath, deconflictedPath); - // this.database.move(oldPath, newPath); - await this.fs.rename(newPath, deconflictedPath); - } else { - // this.database.move(oldPath, newPath); - await this.createParentDirectories(newPath); - } + await this.ensureClearPath(newPath); await this.fs.rename(oldPath, newPath); } @@ -198,7 +186,7 @@ export class FileOperations { ); stem = stem.replace(FileOperations.PARENTHESES_REGEX, ""); - let newName; + let newName = path; do { currentCount++; newName = `${directory}${stem} (${currentCount})${extension}`; diff --git a/frontend/sync-client/src/persistence/database.ts b/frontend/sync-client/src/persistence/database.ts index 42b8be86..73456dc3 100644 --- a/frontend/sync-client/src/persistence/database.ts +++ b/frontend/sync-client/src/persistence/database.ts @@ -130,12 +130,11 @@ export class Database { }, identity?: symbol ): void { - let entry: DocumentRecord | undefined; if (identity !== undefined) { const entry = this.getDocumentByIdentity(identity); this.documents = this.documents.filter( - ({ identity }) => identity !== entry.identity + (doc) => doc.identity !== entry.identity ); if (entry.relativePath !== relativePath) { @@ -161,7 +160,7 @@ export class Database { // We find a match based on relative path and we find one with a different document id // meaning that two documents occupy the same path in terms of in-flight requests so we // need to create a new parallel version. - entry = this.getLatestDocumentByRelativePath(relativePath); + const entry = this.getLatestDocumentByRelativePath(relativePath); if (entry && entry.documentId !== documentId) { this.documents.push({ // `entry` might be undefined if the document is new @@ -238,7 +237,8 @@ export class Database { relativePath: RelativePath, promise: Promise ): void { - const previousEntry = this.getLatestDocumentByRelativePath(relativePath); + const previousEntry = + this.getLatestDocumentByRelativePath(relativePath); const entry = { relativePath, @@ -300,7 +300,8 @@ export class Database { ({ identity }) => identity !== oldDocument.identity ); - const newDocument = this.getLatestDocumentByRelativePath(newRelativePath); + const newDocument = + this.getLatestDocumentByRelativePath(newRelativePath); if (newDocument !== undefined && !newDocument.isDeleted) { throw new Error( `Document already exists at new location: ${newRelativePath}` @@ -338,11 +339,13 @@ export class Database { this.ensureConsistency(); void this.saveData({ documents: this.resolvedDocuments.map( - ({ relativePath, metadata }) => ({ + ({ relativePath, documentId, metadata }) => ({ + documentId, relativePath, - ...metadata + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ...metadata! // resolvedDocuments only returns docs with metadata set }) - ) as StoredDocumentMetadata[], + ), lastSeenUpdateId: this.lastSeenUpdateId }); } diff --git a/frontend/sync-client/src/services/connected-state.ts b/frontend/sync-client/src/services/connected-state.ts index 7e3a28e7..4b62b792 100644 --- a/frontend/sync-client/src/services/connected-state.ts +++ b/frontend/sync-client/src/services/connected-state.ts @@ -23,16 +23,6 @@ export class ConnectedState { }); } - private handleComingOnline() { - this.logger.debug("Sync is enabled"); - this.resolveIsSyncEnabled?.(); - } - - private handleGoingOffline() { - this.logger.debug("Sync is disabled"); - [this.syncIsEnabled, this.resolveIsSyncEnabled] = createPromise(); - } - public getFetchImplementation( fetch: typeof globalThis.fetch, { doRetries = true }: { doRetries: boolean } = { doRetries: true } @@ -48,4 +38,14 @@ export class ConnectedState { return retriedFetch(input); }; } + + private handleComingOnline(): void { + this.logger.debug("Sync is enabled"); + this.resolveIsSyncEnabled?.(); + } + + private handleGoingOffline(): void { + this.logger.debug("Sync is disabled"); + [this.syncIsEnabled, this.resolveIsSyncEnabled] = createPromise(); + } } diff --git a/frontend/sync-client/src/services/types.ts b/frontend/sync-client/src/services/types.ts index 642fd6c2..5c464075 100644 --- a/frontend/sync-client/src/services/types.ts +++ b/frontend/sync-client/src/services/types.ts @@ -4,594 +4,596 @@ */ 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: { - "multipart/form-data": components["schemas"]["CreateDocumentVersionMultipart"]; - }; - }; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["DocumentVersionWithoutContent"]; - }; - }; - 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/json": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - 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"]["DocumentVersionWithoutContent"]; - }; - }; - 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: { - "multipart/form-data": components["schemas"]["UpdateDocumentVersionMultipart"]; - }; - }; - 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: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["DocumentVersionWithoutContent"]; - }; - }; - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SerializedError"]; - }; - }; - }; - }; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/vaults/{vault_id}/documents/{document_id}/json": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - 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?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/vaults/{vault_id}/documents/{document_id}/versions/{version_id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put: { - parameters: { - query?: never; - header: { - authorization: string; - }; - path: { - document_id: string; - vault_id: string; - vault_update_id: number; - }; - 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"]; - }; - }; - }; - }; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/vaults/{vault_id}/documents/{document_id}/versions/{version_id}/content": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put: { - parameters: { - query?: never; - header: { - authorization: string; - }; - path: { - document_id: string; - vault_id: string; - vault_update_id: number; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description byte stream */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/octet-stream": unknown; - }; - }; - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SerializedError"]; - }; - }; - }; - }; - post?: never; - delete?: never; - 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: { + "multipart/form-data": components["schemas"]["CreateDocumentVersionMultipart"]; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DocumentVersionWithoutContent"]; + }; + }; + 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/json": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + 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"]["DocumentVersionWithoutContent"]; + }; + }; + 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: { + "multipart/form-data": components["schemas"]["UpdateDocumentVersionMultipart"]; + }; + }; + 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: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DocumentVersionWithoutContent"]; + }; + }; + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SerializedError"]; + }; + }; + }; + }; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/vaults/{vault_id}/documents/{document_id}/json": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + 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?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/vaults/{vault_id}/documents/{document_id}/versions/{version_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put: { + parameters: { + query?: never; + header: { + authorization: string; + }; + path: { + document_id: string; + vault_id: string; + vault_update_id: number; + }; + 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"]; + }; + }; + }; + }; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/vaults/{vault_id}/documents/{document_id}/versions/{version_id}/content": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put: { + parameters: { + query?: never; + header: { + authorization: string; + }; + path: { + document_id: string; + vault_id: string; + vault_update_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description byte stream */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/octet-stream": unknown; + }; + }; + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SerializedError"]; + }; + }; + }; + }; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; } export type webhooks = Record; export interface components { - schemas: { - Array_of_uint8: number[]; - CreateDocumentVersion: { - contentBase64: string; - /** Format: uuid */ - documentId?: string | null; - relativePath: string; - }; - CreateDocumentVersionMultipart: { - content: components["schemas"]["Array_of_uint8"]; - /** Format: uuid */ - document_id?: string | null; - relative_path: string; - }; - DeleteDocumentVersion: { - relativePath: string; - }; - /** @description Response to an update document request. */ - DocumentUpdateResponse: { - /** 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: 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: uuid */ - documentId: string; - isDeleted: boolean; - relativePath: string; - /** Format: date-time */ - updatedDate: string; - vaultId: string; - /** Format: int64 */ - vaultUpdateId: number; - }; - DocumentVersionWithoutContent: { - /** 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; - /** Format: int64 */ - vault_update_id: number; - }; - PathParams6: { - /** Format: uuid */ - document_id: string; - vault_id: string; - /** Format: int64 */ - vault_update_id: number; - }; - PathParams7: { - /** 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: int64 */ - parentVersionId: number; - relativePath: string; - }; - UpdateDocumentVersionMultipart: { - content: components["schemas"]["Array_of_uint8"]; - /** Format: int64 */ - parentVersionId: number; - relativePath: string; - }; - }; - responses: never; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; + schemas: { + Array_of_uint8: number[]; + CreateDocumentVersion: { + contentBase64: string; + /** Format: uuid */ + documentId?: string | null; + relativePath: string; + }; + CreateDocumentVersionMultipart: { + content: components["schemas"]["Array_of_uint8"]; + /** Format: uuid */ + document_id?: string | null; + relative_path: string; + }; + DeleteDocumentVersion: { + relativePath: string; + }; + /** @description Response to an update document request. */ + DocumentUpdateResponse: + | { + /** 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: 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: uuid */ + documentId: string; + isDeleted: boolean; + relativePath: string; + /** Format: date-time */ + updatedDate: string; + vaultId: string; + /** Format: int64 */ + vaultUpdateId: number; + }; + DocumentVersionWithoutContent: { + /** 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; + /** Format: int64 */ + vault_update_id: number; + }; + PathParams6: { + /** Format: uuid */ + document_id: string; + vault_id: string; + /** Format: int64 */ + vault_update_id: number; + }; + PathParams7: { + /** 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: int64 */ + parentVersionId: number; + relativePath: string; + }; + UpdateDocumentVersionMultipart: { + content: components["schemas"]["Array_of_uint8"]; + /** 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/frontend/sync-client/src/sync-operations/syncer.ts b/frontend/sync-client/src/sync-operations/syncer.ts index d30f8457..5e2327fd 100644 --- a/frontend/sync-client/src/sync-operations/syncer.ts +++ b/frontend/sync-client/src/sync-operations/syncer.ts @@ -44,11 +44,11 @@ export class Syncer { this.syncQueue.concurrency = newSettings.syncConcurrency; }); - this.syncQueue.on("active", () => - { this.remainingOperationsListeners.forEach((listener) => - { listener(this.syncQueue.size); } - ); } - ); + this.syncQueue.on("active", () => { + this.remainingOperationsListeners.forEach((listener) => { + listener(this.syncQueue.size); + }); + }); this.internalSyncer = new UnrestrictedSyncer( logger, @@ -261,7 +261,9 @@ 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(); } diff --git a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts index 1734e25b..ce3f4e60 100644 --- a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts +++ b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts @@ -288,9 +288,7 @@ export class UnrestrictedSyncer { async () => { let localMetadata = getLatestDocument(); - if ( - localMetadata?.metadata !== undefined - ) { + if (localMetadata?.metadata !== undefined) { // If the file exists locally, let's pretend the user has updated it // and deal with remote update/deletion within `unrestrictedSyncLocallyUpdatedFile` if ( @@ -306,6 +304,7 @@ export class UnrestrictedSyncer { return this.unrestrictedSyncLocallyUpdatedFile({ getLatestDocument: () => this.database.getDocumentByIdentity( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion localMetadata!.identity ) }); @@ -334,8 +333,8 @@ export class UnrestrictedSyncer { } if ( - localMetadata?.metadata?.parentVersionId ?? - -1 >= remoteVersion.vaultUpdateId + (localMetadata?.metadata?.parentVersionId ?? -1) >= + remoteVersion.vaultUpdateId ) { this.logger.debug( `Document ${remoteVersion.relativePath} is already more up to date than the fetched version` @@ -347,15 +346,19 @@ export class UnrestrictedSyncer { const [promise, resolve] = createPromise(); + await this.operations.ensureClearPath( + remoteVersion.relativePath + ); + + this.database.getNewResolvedDocumentByRelativePath( + remoteVersion.documentId, + remoteVersion.relativePath, + promise + ); + await this.operations.create( remoteVersion.relativePath, - contentBytes, - () => - { this.database.getNewResolvedDocumentByRelativePath( - remoteVersion.documentId, - remoteVersion.relativePath, - promise - ); } + contentBytes ); const document = diff --git a/frontend/sync-client/tsconfig.json b/frontend/sync-client/tsconfig.json index 40647521..ee31a31e 100644 --- a/frontend/sync-client/tsconfig.json +++ b/frontend/sync-client/tsconfig.json @@ -6,12 +6,10 @@ "allowSyntheticDefaultImports": true, "moduleResolution": "bundler", "lib": [ - "DOM", // to get "fetch" + "DOM" // to get "fetch" ], "declaration": true, "declarationDir": "./dist/types" }, - "exclude": [ - "./dist" - ] -} \ No newline at end of file + "exclude": ["./dist"] +} diff --git a/frontend/test-client/src/agent/mock-client.ts b/frontend/test-client/src/agent/mock-client.ts index 261d4323..941b87d1 100644 --- a/frontend/test-client/src/agent/mock-client.ts +++ b/frontend/test-client/src/agent/mock-client.ts @@ -23,9 +23,10 @@ export class MockClient implements FileSystemOperations { await Promise.all( Object.keys(this.initialSettings).map(async (key) => { + const settingKey = key as keyof SyncSettings; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion return this.client.settings.setSetting( - key as keyof SyncSettings, - this.initialSettings[key as keyof SyncSettings]! + settingKey, + this.initialSettings[settingKey]! // eslint-disable-line @typescript-eslint/no-non-null-assertion ); }) ); @@ -88,10 +89,12 @@ export class MockClient implements FileSystemOperations { const newParts = newContent.split(" ").map((part) => part.trim()); existingParts.forEach((part) => // all changes should be additive - { assert( - newParts.includes(part), - `Part ${part} not found in new content` - ); } + { + assert( + newParts.includes(part), + `Part ${part} not found in new content` + ); + } ); this.client.logger.info(