| 
									
										
										
										
											2025-03-10 16:20:48 +02:00
										 |  |  | import fs from "fs/promises"; | 
					
						
							|  |  |  | import fsExtra from "fs-extra"; | 
					
						
							|  |  |  | import path from "path"; | 
					
						
							| 
									
										
										
										
											2025-03-10 16:37:39 +02:00
										 |  |  | import type NoteMeta from "./src/services/meta/note_meta.js"; | 
					
						
							|  |  |  | import type { NoteMetaFile } from "./src/services/meta/note_meta.js"; | 
					
						
							| 
									
										
										
										
											2025-03-10 17:04:17 +02:00
										 |  |  | import cls from "./src/services/cls.js"; | 
					
						
							|  |  |  | import { initializeTranslations } from "./src/services/i18n.js"; | 
					
						
							| 
									
										
										
										
											2025-03-10 18:51:40 +02:00
										 |  |  | import archiver, { type Archiver } from "archiver"; | 
					
						
							|  |  |  | import type { WriteStream } from "fs"; | 
					
						
							| 
									
										
										
										
											2025-03-10 19:34:10 +02:00
										 |  |  | import debounce from "./src/public/app/services/debounce.js"; | 
					
						
							| 
									
										
										
										
											2025-03-10 16:20:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 20:16:56 +02:00
										 |  |  | const NOTE_ID_USER_GUIDE = "pOsGYCXsbNQG"; | 
					
						
							| 
									
										
										
										
											2025-03-11 21:58:32 +02:00
										 |  |  | const markdownPath = path.join("docs", "User Guide"); | 
					
						
							|  |  |  | const htmlPath = path.join("src", "public", "app", "doc_notes", "en", "User Guide"); | 
					
						
							| 
									
										
										
										
											2025-03-10 16:20:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-28 22:02:29 +02:00
										 |  |  | async function startElectron() { | 
					
						
							|  |  |  |     await import("./electron-main.js"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function main() { | 
					
						
							| 
									
										
										
										
											2025-03-10 17:04:17 +02:00
										 |  |  |     await initializeTranslations(); | 
					
						
							| 
									
										
										
										
											2025-03-10 18:51:40 +02:00
										 |  |  |     const zipBuffer = await createImportZip(); | 
					
						
							| 
									
										
										
										
											2025-03-10 17:08:40 +02:00
										 |  |  |     await initializeDatabase(); | 
					
						
							| 
									
										
										
										
											2025-03-11 20:39:30 +02:00
										 |  |  |     await importData(zipBuffer); | 
					
						
							| 
									
										
										
										
											2025-02-28 22:02:29 +02:00
										 |  |  |     await startElectron(); | 
					
						
							| 
									
										
										
										
											2025-03-11 20:39:30 +02:00
										 |  |  |     await registerHandlers(); | 
					
						
							| 
									
										
										
										
											2025-03-10 17:04:17 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 17:08:40 +02:00
										 |  |  | async function initializeDatabase() { | 
					
						
							| 
									
										
										
										
											2025-03-10 17:04:17 +02:00
										 |  |  |     const sqlInit = (await import("./src/services/sql_init.js")).default; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cls.init(() => { | 
					
						
							| 
									
										
										
										
											2025-03-10 17:08:40 +02:00
										 |  |  |         if (!sqlInit.isDbInitialized()) { | 
					
						
							|  |  |  |             sqlInit.createInitialDatabase(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-03-10 17:04:17 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-03-10 16:20:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-11 20:39:30 +02:00
										 |  |  | function importData(input: Buffer) { | 
					
						
							|  |  |  |     return new Promise<void>((resolve, reject) => { | 
					
						
							|  |  |  |         cls.init(async () => { | 
					
						
							|  |  |  |             const beccaLoader = ((await import("./src/becca/becca_loader.js")).default); | 
					
						
							|  |  |  |             const notes = ((await import("./src/services/notes.js")).default); | 
					
						
							|  |  |  |             beccaLoader.load(); | 
					
						
							|  |  |  |             const becca = ((await import("./src/becca/becca.js")).default); | 
					
						
							|  |  |  |             const utils = ((await import("./src/services/utils.js")).default); | 
					
						
							|  |  |  |             const eraseService = ((await import("./src/services/erase.js")).default); | 
					
						
							|  |  |  |             const deleteId = utils.randomString(10); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             const existingNote = becca.getNote(NOTE_ID_USER_GUIDE); | 
					
						
							|  |  |  |             if (existingNote) { | 
					
						
							|  |  |  |                 existingNote.deleteNote(deleteId); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             eraseService.eraseNotesWithDeleteId(deleteId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             const { note } = notes.createNewNoteWithTarget("into", "none_root", { | 
					
						
							|  |  |  |                 parentNoteId: "root", | 
					
						
							|  |  |  |                 noteId: NOTE_ID_USER_GUIDE, | 
					
						
							|  |  |  |                 title: "User Guide", | 
					
						
							|  |  |  |                 content: "The sub-children of this note are automatically synced.", | 
					
						
							|  |  |  |                 type: "text" | 
					
						
							|  |  |  |             }); | 
					
						
							| 
									
										
										
										
											2025-03-10 18:51:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-11 20:39:30 +02:00
										 |  |  |             const TaskContext = (await import("./src/services/task_context.js")).default; | 
					
						
							|  |  |  |             const { importZip } = ((await import("./src/services/import/zip.js")).default); | 
					
						
							|  |  |  |             const context = new TaskContext("no-report"); | 
					
						
							|  |  |  |             await importZip(context, input, note, { preserveIds: true }); | 
					
						
							| 
									
										
										
										
											2025-03-11 18:26:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-11 20:39:30 +02:00
										 |  |  |             const { runOnDemandChecks } = (await import("./src/services/consistency_checks.js")).default; | 
					
						
							|  |  |  |             await runOnDemandChecks(true); | 
					
						
							| 
									
										
										
										
											2025-03-11 20:42:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             becca.reset(); | 
					
						
							|  |  |  |             beccaLoader.load(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-11 20:39:30 +02:00
										 |  |  |             resolve(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-03-11 18:26:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 17:41:13 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 17:50:58 +02:00
										 |  |  | async function createImportZip() { | 
					
						
							| 
									
										
										
										
											2025-03-11 20:48:40 +02:00
										 |  |  |     const inputFile = "input.zip"; | 
					
						
							| 
									
										
										
										
											2025-03-10 17:50:58 +02:00
										 |  |  |     const archive = archiver("zip", { | 
					
						
							|  |  |  |         zlib: { level: 0 } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-11 21:58:32 +02:00
										 |  |  |     archive.directory(markdownPath, "/"); | 
					
						
							| 
									
										
										
										
											2025-03-10 17:50:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-11 20:48:40 +02:00
										 |  |  |     const outputStream = fsExtra.createWriteStream(inputFile); | 
					
						
							| 
									
										
										
										
											2025-03-10 17:50:58 +02:00
										 |  |  |     archive.pipe(outputStream); | 
					
						
							| 
									
										
										
										
											2025-03-10 18:51:40 +02:00
										 |  |  |     await waitForEnd(archive, outputStream); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-11 20:48:40 +02:00
										 |  |  |     try { | 
					
						
							|  |  |  |         return await fsExtra.readFile(inputFile); | 
					
						
							|  |  |  |     } finally { | 
					
						
							|  |  |  |         await fsExtra.rm(inputFile); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-03-10 18:51:40 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function waitForEnd(archive: Archiver, stream: WriteStream) { | 
					
						
							|  |  |  |     return new Promise<void>(async (res, rej) => { | 
					
						
							|  |  |  |         stream.on("finish", () => res()); | 
					
						
							|  |  |  |         await archive.finalize(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 17:50:58 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-11 21:58:32 +02:00
										 |  |  | async function exportData(format: "html" | "markdown", outputPath: string) { | 
					
						
							| 
									
										
										
										
											2025-03-10 16:20:48 +02:00
										 |  |  |     const zipFilePath = "output.zip"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 16:31:44 +02:00
										 |  |  |     const deferred = (await import("./src/services/utils.js")).deferred; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try { | 
					
						
							| 
									
										
										
										
											2025-03-11 21:58:32 +02:00
										 |  |  |         await fsExtra.remove(outputPath); | 
					
						
							|  |  |  |         await fsExtra.mkdir(outputPath); | 
					
						
							| 
									
										
										
										
											2025-03-10 16:31:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // First export as zip.
 | 
					
						
							|  |  |  |         const { exportToZipFile } = (await import("./src/services/export/zip.js")).default; | 
					
						
							| 
									
										
										
										
											2025-03-11 21:58:32 +02:00
										 |  |  |         await exportToZipFile(NOTE_ID_USER_GUIDE, format, zipFilePath); | 
					
						
							| 
									
										
										
										
											2025-03-10 16:31:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const promise = deferred<void>() | 
					
						
							|  |  |  |         setTimeout(async () => { | 
					
						
							|  |  |  |             // Then extract the zip.
 | 
					
						
							|  |  |  |             const { readZipFile, readContent } = (await import("./src/services/import/zip.js")); | 
					
						
							|  |  |  |             await readZipFile(await fs.readFile(zipFilePath), async (zip, entry) => { | 
					
						
							|  |  |  |                 // We ignore directories since they can appear out of order anyway.
 | 
					
						
							|  |  |  |                 if (!entry.fileName.endsWith("/")) { | 
					
						
							| 
									
										
										
										
											2025-03-11 21:58:32 +02:00
										 |  |  |                     const destPath = path.join(outputPath, entry.fileName); | 
					
						
							| 
									
										
										
										
											2025-03-10 16:31:44 +02:00
										 |  |  |                     const fileContent = await readContent(zip, entry); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     await fsExtra.mkdirs(path.dirname(destPath)); | 
					
						
							|  |  |  |                     await fs.writeFile(destPath, fileContent); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 zip.readEntry(); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |             promise.resolve(); | 
					
						
							|  |  |  |         }, 1000); | 
					
						
							|  |  |  |         await promise; | 
					
						
							|  |  |  |     } finally { | 
					
						
							|  |  |  |         if (await fsExtra.exists(zipFilePath)) { | 
					
						
							|  |  |  |             await fsExtra.rm(zipFilePath); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-03-10 16:37:39 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     await cleanUpMeta(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function cleanUpMeta() { | 
					
						
							| 
									
										
										
										
											2025-03-11 21:58:32 +02:00
										 |  |  |     const metaPath = path.join(markdownPath, "!!!meta.json"); | 
					
						
							| 
									
										
										
										
											2025-03-10 16:37:39 +02:00
										 |  |  |     const meta = JSON.parse(await fs.readFile(metaPath, "utf-8")) as NoteMetaFile; | 
					
						
							|  |  |  |     for (const file of meta.files) { | 
					
						
							|  |  |  |         traverse(file); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function traverse(el: NoteMeta) { | 
					
						
							|  |  |  |         for (const child of el.children || []) { | 
					
						
							|  |  |  |             traverse(child); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         el.isExpanded = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await fs.writeFile(metaPath, JSON.stringify(meta, null, 4)); | 
					
						
							| 
									
										
										
										
											2025-02-28 22:02:29 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-11 20:39:30 +02:00
										 |  |  | async function registerHandlers() { | 
					
						
							|  |  |  |     const events = (await import("./src/services/events.js")).default; | 
					
						
							| 
									
										
										
										
											2025-03-16 14:45:15 +02:00
										 |  |  |     const eraseService = (await import("./src/services/erase.js")).default; | 
					
						
							| 
									
										
										
										
											2025-03-11 21:58:32 +02:00
										 |  |  |     const debouncer = debounce(async () => { | 
					
						
							| 
									
										
										
										
											2025-03-11 20:39:30 +02:00
										 |  |  |         console.log("Exporting data"); | 
					
						
							| 
									
										
										
										
											2025-03-16 14:45:15 +02:00
										 |  |  |         eraseService.eraseUnusedAttachmentsNow(); | 
					
						
							| 
									
										
										
										
											2025-03-11 21:58:32 +02:00
										 |  |  |         await exportData("markdown", markdownPath); | 
					
						
							|  |  |  |         await exportData("html", htmlPath); | 
					
						
							| 
									
										
										
										
											2025-03-11 20:39:30 +02:00
										 |  |  |     }, 10_000);; | 
					
						
							|  |  |  |     events.subscribe(events.ENTITY_CHANGED, async (e) => { | 
					
						
							| 
									
										
										
										
											2025-03-11 20:40:25 +02:00
										 |  |  |         if (e.entityName === "options") { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-11 20:39:30 +02:00
										 |  |  |         console.log("Got entity changed ", e); | 
					
						
							|  |  |  |         debouncer(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-28 22:02:29 +02:00
										 |  |  | await main(); |