diff --git a/apps/client/src/services/protected_session.ts b/apps/client/src/services/protected_session.ts index 94148a455..1e1984ae5 100644 --- a/apps/client/src/services/protected_session.ts +++ b/apps/client/src/services/protected_session.ts @@ -111,7 +111,7 @@ ws.subscribeToMessages(async (message) => { return; } - const isProtecting = message.data.protect; + const isProtecting = message.data?.protect; const title = isProtecting ? t("protected_session.protecting-title") : t("protected_session.unprotecting-title"); if (message.type === "taskError") { diff --git a/apps/server/src/becca/entities/bbranch.ts b/apps/server/src/becca/entities/bbranch.ts index 00e3ec4b7..cd50fe09b 100644 --- a/apps/server/src/becca/entities/bbranch.ts +++ b/apps/server/src/becca/entities/bbranch.ts @@ -137,13 +137,13 @@ class BBranch extends AbstractBeccaEntity { * * @returns true if note has been deleted, false otherwise */ - deleteBranch(deleteId?: string, taskContext?: TaskContext): boolean { + deleteBranch(deleteId?: string, taskContext?: TaskContext<"deleteNotes">): boolean { if (!deleteId) { deleteId = utils.randomString(10); } if (!taskContext) { - taskContext = new TaskContext("no-progress-reporting"); + taskContext = new TaskContext("no-progress-reporting", "deleteNotes", null); } taskContext.increaseProgressCount(); diff --git a/apps/server/src/becca/entities/bnote.ts b/apps/server/src/becca/entities/bnote.ts index 68c82702b..1a724b1b0 100644 --- a/apps/server/src/becca/entities/bnote.ts +++ b/apps/server/src/becca/entities/bnote.ts @@ -1512,7 +1512,7 @@ class BNote extends AbstractBeccaEntity { * * @param deleteId - optional delete identified */ - deleteNote(deleteId: string | null = null, taskContext: TaskContext | null = null) { + deleteNote(deleteId: string | null = null, taskContext: TaskContext<"deleteNotes"> | null = null) { if (this.isDeleted) { return; } @@ -1522,7 +1522,7 @@ class BNote extends AbstractBeccaEntity { } if (!taskContext) { - taskContext = new TaskContext("no-progress-reporting"); + taskContext = new TaskContext("no-progress-reporting", "deleteNotes", null); } // needs to be run before branches and attributes are deleted and thus attached relations disappear diff --git a/apps/server/src/etapi/notes.ts b/apps/server/src/etapi/notes.ts index e7a1c0a1c..07d6a3e68 100644 --- a/apps/server/src/etapi/notes.ts +++ b/apps/server/src/etapi/notes.ts @@ -108,7 +108,7 @@ function register(router: Router) { return res.sendStatus(204); } - note.deleteNote(null, new TaskContext("no-progress-reporting")); + note.deleteNote(null, new TaskContext("no-progress-reporting", "deleteNotes", null)); res.sendStatus(204); }); @@ -153,7 +153,7 @@ function register(router: Router) { throw new eu.EtapiError(400, "UNRECOGNIZED_EXPORT_FORMAT", `Unrecognized export format '${format}', supported values are 'html' (default) or 'markdown'.`); } - const taskContext = new TaskContext("no-progress-reporting"); + const taskContext = new TaskContext("no-progress-reporting", "export", null); // technically a branch is being exported (includes prefix), but it's such a minor difference yet usability pain // (e.g. branchIds are not seen in UI), that we export "note export" instead. @@ -164,7 +164,7 @@ function register(router: Router) { eu.route(router, "post", "/etapi/notes/:noteId/import", (req, res, next) => { const note = eu.getAndCheckNote(req.params.noteId); - const taskContext = new TaskContext("no-progress-reporting"); + const taskContext = new TaskContext("no-progress-reporting", "importNotes", null); zipImportService.importZip(taskContext, req.body, note).then((importedNote) => { res.status(201).json({ diff --git a/apps/server/src/routes/api/branches.ts b/apps/server/src/routes/api/branches.ts index bed1c93b7..ac6da765f 100644 --- a/apps/server/src/routes/api/branches.ts +++ b/apps/server/src/routes/api/branches.ts @@ -236,7 +236,7 @@ function deleteBranch(req: Request) { const eraseNotes = req.query.eraseNotes === "true"; const branch = becca.getBranchOrThrow(req.params.branchId); - const taskContext = TaskContext.getInstance(req.query.taskId as string, "deleteNotes"); + const taskContext = TaskContext.getInstance(req.query.taskId as string, "deleteNotes", null); const deleteId = utils.randomString(10); let noteDeleted; @@ -251,7 +251,7 @@ function deleteBranch(req: Request) { } if (last) { - taskContext.taskSucceeded(); + taskContext.taskSucceeded(null); } return { diff --git a/apps/server/src/routes/api/export.ts b/apps/server/src/routes/api/export.ts index 7433cd552..4bc0c2177 100644 --- a/apps/server/src/routes/api/export.ts +++ b/apps/server/src/routes/api/export.ts @@ -23,7 +23,7 @@ function exportBranch(req: Request, res: Response) { return; } - const taskContext = new TaskContext(taskId, "export"); + const taskContext = new TaskContext(taskId, "export", null); try { if (type === "subtree" && (format === "html" || format === "markdown")) { diff --git a/apps/server/src/routes/api/import.ts b/apps/server/src/routes/api/import.ts index c7253f2d6..273dc1e1d 100644 --- a/apps/server/src/routes/api/import.ts +++ b/apps/server/src/routes/api/import.ts @@ -116,7 +116,7 @@ function importAttachmentsToNote(req: Request) { } const parentNote = becca.getNoteOrThrow(parentNoteId); - const taskContext = TaskContext.getInstance(taskId, "importAttachment", options); + const taskContext = TaskContext.getInstance(taskId, "importNotes", options); // unlike in note import, we let the events run, because a huge number of attachments is not likely diff --git a/apps/server/src/routes/api/notes.ts b/apps/server/src/routes/api/notes.ts index 8a426dea3..3c6db4054 100644 --- a/apps/server/src/routes/api/notes.ts +++ b/apps/server/src/routes/api/notes.ts @@ -184,7 +184,7 @@ function deleteNote(req: Request) { if (typeof taskId !== "string") { throw new ValidationError("Missing or incorrect type for task ID."); } - const taskContext = TaskContext.getInstance(taskId, "deleteNotes"); + const taskContext = TaskContext.getInstance(taskId, "deleteNotes", null); note.deleteNote(deleteId, taskContext); @@ -193,16 +193,16 @@ function deleteNote(req: Request) { } if (last) { - taskContext.taskSucceeded(); + taskContext.taskSucceeded(null); } } function undeleteNote(req: Request) { - const taskContext = TaskContext.getInstance(utils.randomString(10), "undeleteNotes"); + const taskContext = TaskContext.getInstance(utils.randomString(10), "undeleteNotes", null); noteService.undeleteNote(req.params.noteId, taskContext); - taskContext.taskSucceeded(); + taskContext.taskSucceeded(null); } function sortChildNotes(req: Request) { @@ -226,7 +226,7 @@ function protectNote(req: Request) { noteService.protectNoteRecursively(note, protect, includingSubTree, taskContext); - taskContext.taskSucceeded(); + taskContext.taskSucceeded(null); } function setNoteTypeMime(req: Request) { diff --git a/apps/server/src/services/export/opml.ts b/apps/server/src/services/export/opml.ts index 74d2b2c4e..52e60a60f 100644 --- a/apps/server/src/services/export/opml.ts +++ b/apps/server/src/services/export/opml.ts @@ -6,7 +6,7 @@ import type TaskContext from "../task_context.js"; import type BBranch from "../../becca/entities/bbranch.js"; import type { Response } from "express"; -function exportToOpml(taskContext: TaskContext, branch: BBranch, version: string, res: Response) { +function exportToOpml(taskContext: TaskContext<"export">, branch: BBranch, version: string, res: Response) { if (!["1.0", "2.0"].includes(version)) { throw new Error(`Unrecognized OPML version ${version}`); } @@ -77,7 +77,7 @@ function exportToOpml(taskContext: TaskContext, branch: BBranch, version: string `); res.end(); - taskContext.taskSucceeded(); + taskContext.taskSucceeded(null); } function prepareText(text: string) { diff --git a/apps/server/src/services/export/single.ts b/apps/server/src/services/export/single.ts index b626bf919..678fb39e5 100644 --- a/apps/server/src/services/export/single.ts +++ b/apps/server/src/services/export/single.ts @@ -10,7 +10,7 @@ import type BBranch from "../../becca/entities/bbranch.js"; import type { Response } from "express"; import type BNote from "../../becca/entities/bnote.js"; -function exportSingleNote(taskContext: TaskContext, branch: BBranch, format: "html" | "markdown", res: Response) { +function exportSingleNote(taskContext: TaskContext<"export">, branch: BBranch, format: "html" | "markdown", res: Response) { const note = branch.getNote(); if (note.type === "image" || note.type === "file") { @@ -30,7 +30,7 @@ function exportSingleNote(taskContext: TaskContext, branch: BBranch, format: "ht res.send(payload); taskContext.increaseProgressCount(); - taskContext.taskSucceeded(); + taskContext.taskSucceeded(null); } export function mapByNoteType(note: BNote, content: string | Buffer, format: "html" | "markdown") { diff --git a/apps/server/src/services/export/zip.ts b/apps/server/src/services/export/zip.ts index 91d01c8c7..116a841b2 100644 --- a/apps/server/src/services/export/zip.ts +++ b/apps/server/src/services/export/zip.ts @@ -40,7 +40,7 @@ export interface AdvancedExportOptions { customRewriteLinks?: (originalRewriteLinks: RewriteLinksFn, getNoteTargetUrl: (targetNoteId: string, sourceMeta: NoteMeta) => string | null) => RewriteLinksFn; } -async function exportToZip(taskContext: TaskContext, branch: BBranch, format: "html" | "markdown", res: Response | fs.WriteStream, setHeaders = true, zipExportOptions?: AdvancedExportOptions) { +async function exportToZip(taskContext: TaskContext<"export">, branch: BBranch, format: "html" | "markdown", res: Response | fs.WriteStream, setHeaders = true, zipExportOptions?: AdvancedExportOptions) { if (!["html", "markdown"].includes(format)) { throw new ValidationError(`Only 'html' and 'markdown' allowed as export format, '${format}' given`); } @@ -611,7 +611,7 @@ ${markdownContent}`; archive.pipe(res); await archive.finalize(); - taskContext.taskSucceeded(); + taskContext.taskSucceeded(null); } catch (e: unknown) { const message = `Export failed with error: ${e instanceof Error ? e.message : String(e)}`; log.error(message); @@ -627,7 +627,7 @@ ${markdownContent}`; async function exportToZipFile(noteId: string, format: "markdown" | "html", zipFilePath: string, zipExportOptions?: AdvancedExportOptions) { const fileOutputStream = fs.createWriteStream(zipFilePath); - const taskContext = new TaskContext("no-progress-reporting"); + const taskContext = new TaskContext("no-progress-reporting", "export", null); const note = becca.getNote(noteId); diff --git a/apps/server/src/services/import/enex.ts b/apps/server/src/services/import/enex.ts index 4699ca32e..5a13e0960 100644 --- a/apps/server/src/services/import/enex.ts +++ b/apps/server/src/services/import/enex.ts @@ -55,7 +55,7 @@ interface Note { let note: Partial = {}; let resource: Resource; -function importEnex(taskContext: TaskContext, file: File, parentNote: BNote): Promise { +function importEnex(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote): Promise { const saxStream = sax.createStream(true); const rootNoteTitle = file.originalname.toLowerCase().endsWith(".enex") ? file.originalname.substr(0, file.originalname.length - 5) : file.originalname; diff --git a/apps/server/src/services/import/mime.ts b/apps/server/src/services/import/mime.ts index cce580a08..0a129ae1e 100644 --- a/apps/server/src/services/import/mime.ts +++ b/apps/server/src/services/import/mime.ts @@ -91,14 +91,14 @@ function getMime(fileName: string) { return mimeFromExt || mimeTypes.lookup(fileNameLc); } -function getType(options: TaskData, mime: string): NoteType { +function getType(options: TaskData<"importNotes">, mime: string): NoteType { const mimeLc = mime?.toLowerCase(); switch (true) { - case options.textImportedAsText && ["text/html", "text/markdown", "text/x-markdown", "text/mdx"].includes(mimeLc): + case options?.textImportedAsText && ["text/html", "text/markdown", "text/x-markdown", "text/mdx"].includes(mimeLc): return "text"; - case options.codeImportedAsCode && CODE_MIME_TYPES.has(mimeLc): + case options?.codeImportedAsCode && CODE_MIME_TYPES.has(mimeLc): return "code"; case mime.startsWith("image/"): diff --git a/apps/server/src/services/import/opml.ts b/apps/server/src/services/import/opml.ts index 934578c70..130eb8197 100644 --- a/apps/server/src/services/import/opml.ts +++ b/apps/server/src/services/import/opml.ts @@ -28,7 +28,7 @@ interface OpmlOutline { outline: OpmlOutline[]; } -async function importOpml(taskContext: TaskContext, fileBuffer: string | Buffer, parentNote: BNote) { +async function importOpml(taskContext: TaskContext<"importNotes">, fileBuffer: string | Buffer, parentNote: BNote) { const xml = await new Promise(function (resolve, reject) { parseString(fileBuffer, function (err: any, result: OpmlXml) { if (err) { diff --git a/apps/server/src/services/import/single.ts b/apps/server/src/services/import/single.ts index 7603cd625..ac52a43f4 100644 --- a/apps/server/src/services/import/single.ts +++ b/apps/server/src/services/import/single.ts @@ -14,7 +14,7 @@ import htmlSanitizer from "../html_sanitizer.js"; import type { File } from "./common.js"; import type { NoteType } from "@triliumnext/commons"; -function importSingleFile(taskContext: TaskContext, file: File, parentNote: BNote) { +function importSingleFile(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) { const mime = mimeService.getMime(file.originalname) || file.mimetype; if (taskContext?.data?.textImportedAsText) { @@ -42,7 +42,7 @@ function importSingleFile(taskContext: TaskContext, file: File, parentNote: BNot return importFile(taskContext, file, parentNote); } -function importImage(file: File, parentNote: BNote, taskContext: TaskContext) { +function importImage(file: File, parentNote: BNote, taskContext: TaskContext<"importNotes">) { if (typeof file.buffer === "string") { throw new Error("Invalid file content for image."); } @@ -53,7 +53,7 @@ function importImage(file: File, parentNote: BNote, taskContext: TaskContext) { return note; } -function importFile(taskContext: TaskContext, file: File, parentNote: BNote) { +function importFile(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) { const originalName = file.originalname; const { note } = noteService.createNewNote({ @@ -72,7 +72,7 @@ function importFile(taskContext: TaskContext, file: File, parentNote: BNote) { return note; } -function importCodeNote(taskContext: TaskContext, file: File, parentNote: BNote) { +function importCodeNote(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) { const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); const content = processStringOrBuffer(file.buffer); const detectedMime = mimeService.getMime(file.originalname) || file.mimetype; @@ -97,7 +97,7 @@ function importCodeNote(taskContext: TaskContext, file: File, parentNote: BNote) return note; } -function importCustomType(taskContext: TaskContext, file: File, parentNote: BNote, type: NoteType, mime: string) { +function importCustomType(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote, type: NoteType, mime: string) { const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); const content = processStringOrBuffer(file.buffer); @@ -115,7 +115,7 @@ function importCustomType(taskContext: TaskContext, file: File, parentNote: BNot return note; } -function importPlainText(taskContext: TaskContext, file: File, parentNote: BNote) { +function importPlainText(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) { const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); const plainTextContent = processStringOrBuffer(file.buffer); const htmlContent = convertTextToHtml(plainTextContent); @@ -150,7 +150,7 @@ function convertTextToHtml(text: string) { return text; } -function importMarkdown(taskContext: TaskContext, file: File, parentNote: BNote) { +function importMarkdown(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) { const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); const markdownContent = processStringOrBuffer(file.buffer); @@ -174,7 +174,7 @@ function importMarkdown(taskContext: TaskContext, file: File, parentNote: BNote) return note; } -function importHtml(taskContext: TaskContext, file: File, parentNote: BNote) { +function importHtml(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) { let content = processStringOrBuffer(file.buffer); // Try to get title from HTML first, fall back to filename @@ -202,7 +202,7 @@ function importHtml(taskContext: TaskContext, file: File, parentNote: BNote) { return note; } -function importAttachment(taskContext: TaskContext, file: File, parentNote: BNote) { +function importAttachment(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) { const mime = mimeService.getMime(file.originalname) || file.mimetype; if (mime.startsWith("image/") && typeof file.buffer !== "string") { diff --git a/apps/server/src/services/import/zip.ts b/apps/server/src/services/import/zip.ts index b2d83bdc6..c1ac90b91 100644 --- a/apps/server/src/services/import/zip.ts +++ b/apps/server/src/services/import/zip.ts @@ -30,7 +30,7 @@ interface ImportZipOpts { preserveIds?: boolean; } -async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRootNote: BNote, opts?: ImportZipOpts): Promise { +async function importZip(taskContext: TaskContext<"importNotes">, fileBuffer: Buffer, importRootNote: BNote, opts?: ImportZipOpts): Promise { /** maps from original noteId (in ZIP file) to newly generated noteId */ const noteIdMap: Record = {}; /** type maps from original attachmentId (in ZIP file) to newly generated attachmentId */ @@ -174,7 +174,7 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo return noteId; } - function detectFileTypeAndMime(taskContext: TaskContext, filePath: string) { + function detectFileTypeAndMime(taskContext: TaskContext<"importNotes">, filePath: string) { const mime = mimeService.getMime(filePath) || "application/octet-stream"; const type = mimeService.getType(taskContext.data || {}, mime); diff --git a/apps/server/src/services/notes.ts b/apps/server/src/services/notes.ts index e225cdb52..3ecf98e0a 100644 --- a/apps/server/src/services/notes.ts +++ b/apps/server/src/services/notes.ts @@ -296,7 +296,7 @@ function createNewNoteWithTarget(target: "into" | "after" | "before", targetBran } } -function protectNoteRecursively(note: BNote, protect: boolean, includingSubTree: boolean, taskContext: TaskContext) { +function protectNoteRecursively(note: BNote, protect: boolean, includingSubTree: boolean, taskContext: TaskContext<"protectNotes">) { protectNote(note, protect); taskContext.increaseProgressCount(); @@ -765,7 +765,7 @@ function updateNoteData(noteId: string, content: string, attachments: Attachment } } -function undeleteNote(noteId: string, taskContext: TaskContext) { +function undeleteNote(noteId: string, taskContext: TaskContext<"undeleteNotes">) { const noteRow = sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]); if (!noteRow.isDeleted || !noteRow.deleteId) { @@ -785,7 +785,7 @@ function undeleteNote(noteId: string, taskContext: TaskContext) { } } -function undeleteBranch(branchId: string, deleteId: string, taskContext: TaskContext) { +function undeleteBranch(branchId: string, deleteId: string, taskContext: TaskContext<"undeleteNotes">) { const branchRow = sql.getRow("SELECT * FROM branches WHERE branchId = ?", [branchId]); if (!branchRow.isDeleted) { diff --git a/apps/server/src/services/sql_init.ts b/apps/server/src/services/sql_init.ts index 541e487a0..926d61bba 100644 --- a/apps/server/src/services/sql_init.ts +++ b/apps/server/src/services/sql_init.ts @@ -122,7 +122,7 @@ async function createInitialDatabase(skipDemoDb?: boolean) { log.info("Importing demo content ..."); - const dummyTaskContext = new TaskContext("no-progress-reporting", "import", false); + const dummyTaskContext = new TaskContext("no-progress-reporting", "importNotes", null); if (demoFile) { await zipImportService.importZip(dummyTaskContext, demoFile, rootNote); diff --git a/apps/server/src/services/task_context.ts b/apps/server/src/services/task_context.ts index 60ece320b..79122895b 100644 --- a/apps/server/src/services/task_context.ts +++ b/apps/server/src/services/task_context.ts @@ -1,20 +1,20 @@ "use strict"; -import type { TaskType } from "@triliumnext/commons"; +import type { TaskData, TaskResult, TaskType, WebSocketMessage } from "@triliumnext/commons"; import ws from "./ws.js"; // taskId => TaskContext -const taskContexts: Record> = {}; +const taskContexts: Record> = {}; -class TaskContext { +class TaskContext { private taskId: string; private taskType: TaskType; private progressCount: number; private lastSentCountTs: number; - data: TaskData | null; + data: TaskData; noteDeletionHandlerTriggered: boolean; - constructor(taskId: string, taskType: TaskTypeT, data: {} | null = {}) { + constructor(taskId: string, taskType: T, data: TaskData) { this.taskId = taskId; this.taskType = taskType; this.data = data; @@ -31,7 +31,7 @@ class TaskContext { this.increaseProgressCount(); } - static getInstance(taskId: string, taskType: TaskTypeT, data: {} | null = null): TaskContext { + static getInstance(taskId: string, taskType: T, data: TaskData): TaskContext { if (!taskContexts[taskId]) { taskContexts[taskId] = new TaskContext(taskId, taskType, data); } @@ -51,7 +51,7 @@ class TaskContext { taskType: this.taskType, data: this.data, progressCount: this.progressCount - }); + } as WebSocketMessage); } } @@ -62,17 +62,17 @@ class TaskContext { taskType: this.taskType, data: this.data, message - }); + } as WebSocketMessage); } - taskSucceeded(result?: string | Record) { + taskSucceeded(result: TaskResult) { ws.sendMessageToAllClients({ type: "taskSucceeded", taskId: this.taskId, taskType: this.taskType, data: this.data, result - }); + } as WebSocketMessage); } } diff --git a/apps/server/src/services/ws.ts b/apps/server/src/services/ws.ts index f89b44869..9dfcbc019 100644 --- a/apps/server/src/services/ws.ts +++ b/apps/server/src/services/ws.ts @@ -13,7 +13,7 @@ import type { IncomingMessage, Server as HttpServer } from "http"; import { WebSocketMessage, type EntityChange } from "@triliumnext/commons"; let webSocketServer!: WebSocketServer; -let lastSyncedPush: number | null = null; +let lastSyncedPush: number; type SessionParser = (req: IncomingMessage, params: {}, cb: () => void) => void; function init(httpServer: HttpServer, sessionParser: SessionParser) { diff --git a/packages/commons/src/lib/ws_api.ts b/packages/commons/src/lib/ws_api.ts index dca860784..67beb0b42 100644 --- a/packages/commons/src/lib/ws_api.ts +++ b/packages/commons/src/lib/ws_api.ts @@ -26,37 +26,64 @@ export interface EntityChangeRecord { entity?: EntityRow; } -type TaskStatus = { +type TaskDataDefinitions = { + empty: null, + deleteNotes: null, + undeleteNotes: null, + export: null, + protectNotes: { + protect: boolean; + } + importNotes: { + textImportedAsText?: boolean; + codeImportedAsCode?: boolean; + replaceUnderscoresWithSpaces?: boolean; + shrinkImages?: boolean; + safeImport?: boolean; + } | null, + importAttachments: null +} + +type TaskResultDefinitions = { + empty: null, + deleteNotes: null, + undeleteNotes: null, + export: null, + protectNotes: null, + importNotes: { + parentNoteId?: string; + importedNoteId?: string + }; + importAttachments: { + parentNoteId?: string; + importedNoteId?: string + }; +} + +export type TaskType = keyof TaskDataDefinitions | keyof TaskResultDefinitions; +export type TaskData = TaskDataDefinitions[T]; +export type TaskResult = TaskResultDefinitions[T]; + +type TaskDefinition = { type: "taskProgressCount", taskId: string; - taskType: TypeT; - data: DataT, + taskType: T; + data: TaskData, progressCount: number } | { type: "taskError", taskId: string; - taskType: TypeT; - data: DataT; + taskType: T; + data: TaskData, message: string; } | { type: "taskSucceeded", taskId: string; - taskType: TypeT; - data: DataT; - result: ResultT; + taskType: T; + data: TaskData, + result: TaskResult; } -type TaskDefinitions = - TaskStatus<"protectNotes", { protect: boolean; }, null> - | TaskStatus<"importNotes", null, { importedNoteId: string }> - | TaskStatus<"importAttachments", null, { parentNoteId?: string; importedNoteId: string }> - | TaskStatus<"deleteNotes", null, null> - | TaskStatus<"undeleteNotes", null, null> - | TaskStatus<"export", null, null> -; - -export type TaskType = TaskDefinitions["taskType"]; - export interface OpenedFileUpdateStatus { entityType: string; entityId: string; @@ -64,7 +91,16 @@ export interface OpenedFileUpdateStatus { filePath: string; } -export type WebSocketMessage = TaskDefinitions | { +type AllTaskDefinitions = + | TaskDefinition<"empty"> + | TaskDefinition<"deleteNotes"> + | TaskDefinition<"undeleteNotes"> + | TaskDefinition<"export"> + | TaskDefinition<"protectNotes"> + | TaskDefinition<"importNotes"> + | TaskDefinition<"importAttachments">; + +export type WebSocketMessage = AllTaskDefinitions | { type: "ping" } | { type: "frontend-update",