From 7a2b5e731e0085d5b49e98853aea11adfdad5632 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 22 Dec 2024 15:45:54 +0200 Subject: [PATCH] chore(code): fix more js & ts files --- .editorconfig | 2 +- bin/copy-dist.ts | 8 +- bin/update-build-info.ts | 2 +- bin/update-nightly-version.ts | 10 +- bin/watch-dist.ts | 2 +- electron-main.ts | 2 +- electron.ts | 6 +- integration-tests/auth.setup.ts | 6 +- integration-tests/duplicate.spec.ts | 4 +- integration-tests/help.spec.ts | 4 +- integration-tests/i18n.spec.ts | 6 +- integration-tests/katex.disabled.ts | 4 +- integration-tests/settings.spec.ts | 2 +- integration-tests/tree.spec.ts | 2 +- integration-tests/update_check.spec.ts | 2 +- loader-register.js | 2 +- playwright.config.ts | 12 +- spec/etapi/import.ts | 8 +- spec/etapi/notes.ts | 58 +-- spec/search/becca_mocking.ts | 40 +- spec/search/lexer.spec.ts | 182 ++++---- spec/services/export/md.spec.ts | 2 +- spec/support/etapi.ts | 30 +- spec/support/utils.spec.ts | 2 +- spec/support/utils.ts | 10 +- src/becca/becca-interface.ts | 12 +- src/becca/entities/abstract_becca_entity.ts | 36 +- src/becca/entities/battachment.ts | 6 +- src/becca/entities/bbranch.ts | 26 +- src/becca/entities/bnote.ts | 466 ++++++++++---------- src/becca/entities/brevision.ts | 40 +- src/becca/entities/rows.ts | 2 +- src/becca/similarity.ts | 12 +- src/errors/not_found_error.ts | 2 +- src/errors/validation_error.ts | 2 +- src/etapi/etapi-interface.ts | 2 +- src/express.d.ts | 4 +- src/main.ts | 2 +- src/share/shaca/shaca-interface.ts | 2 +- src/share/shaca/shaca_loader.ts | 32 +- src/share/sql.ts | 2 +- src/types.d.ts | 2 +- src/www.ts | 32 +- tests-examples/demo-todo-app.spec.ts | 58 +-- 44 files changed, 574 insertions(+), 574 deletions(-) diff --git a/.editorconfig b/.editorconfig index 677e36e29..5a2fd6cb9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,6 @@ root = true -[*] +[*.{js,ts}] charset = utf-8 end_of_line = lf indent_size = 4 diff --git a/bin/copy-dist.ts b/bin/copy-dist.ts index 9e272d222..69ca710b7 100644 --- a/bin/copy-dist.ts +++ b/bin/copy-dist.ts @@ -23,7 +23,7 @@ async function copyNodeModuleFileOrFolder(source: string) { } const copy = async () => { - for (const srcFile of fs.readdirSync("build")) { + for (const srcFile of fs.readdirSync("build")) { const destFile = path.join(DEST_DIR, path.basename(srcFile)); log(`Copying source ${srcFile} -> ${destFile}.`); fs.copySync(path.join("build", srcFile), destFile, { recursive: true }); @@ -45,11 +45,11 @@ const copy = async () => { for (const dir of srcDirsToCopy) { log(`Copying ${dir}`); await fs.copy(dir, path.join(DEST_DIR_SRC, path.basename(dir))); - } + } /** - * Directories to be copied relative to the project root into /src/public/app-dist. - */ + * Directories to be copied relative to the project root into /src/public/app-dist. + */ const publicDirsToCopy = [ "./src/public/app/doc_notes" ]; const PUBLIC_DIR = path.join(DEST_DIR, "src", "public", "app-dist"); for (const dir of publicDirsToCopy) { diff --git a/bin/update-build-info.ts b/bin/update-build-info.ts index d1c327b7d..25356c468 100644 --- a/bin/update-build-info.ts +++ b/bin/update-build-info.ts @@ -22,4 +22,4 @@ export default { }; `; -fs.writeFileSync("src/services/build.ts", output); \ No newline at end of file +fs.writeFileSync("src/services/build.ts", output); diff --git a/bin/update-nightly-version.ts b/bin/update-nightly-version.ts index aad8da3cf..c700e3b22 100644 --- a/bin/update-nightly-version.ts +++ b/bin/update-nightly-version.ts @@ -1,14 +1,14 @@ /** * @module - * + * * The nightly version works uses the version described in `package.json`, just like any release. * The problem with this approach is that production builds have a very aggressive cache, and * usually running the nightly with this cached version of the application will mean that the * user might run into module not found errors or styling errors caused by an old cache. - * + * * This script is supposed to be run in the CI, which will update locally the version field of * `package.json` to contain the date. For example, `0.90.9-beta` will become `0.90.9-test-YYMMDD-HHMMSS`. - * + * */ import { fileURLToPath } from "url"; @@ -33,7 +33,7 @@ function processVersion(version) { function main() { const scriptDir = dirname(fileURLToPath(import.meta.url)); const packageJsonPath = join(scriptDir, "..", "package.json"); - + // Read the version from package.json and process it. const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")); const currentVersion = packageJson.version; @@ -43,7 +43,7 @@ function main() { // Write the adjusted version back in. packageJson.version = adjustedVersion; - const formattedJson = JSON.stringify(packageJson, null, 4); + const formattedJson = JSON.stringify(packageJson, null, 4); fs.writeFileSync(packageJsonPath, formattedJson); } diff --git a/bin/watch-dist.ts b/bin/watch-dist.ts index 490ffbe5d..9faa8d7a2 100644 --- a/bin/watch-dist.ts +++ b/bin/watch-dist.ts @@ -15,4 +15,4 @@ const sourceDir = "src/public"; chokidar .watch(sourceDir) .on("change", onFileChanged); -console.log(`Watching for changes to ${sourceDir}...`); \ No newline at end of file +console.log(`Watching for changes to ${sourceDir}...`); diff --git a/electron-main.ts b/electron-main.ts index 6fff6ae6b..58fae2dd9 100644 --- a/electron-main.ts +++ b/electron-main.ts @@ -1,4 +1,4 @@ import { initializeTranslations } from "./src/services/i18n.js"; await initializeTranslations(); -await import("./electron.js") \ No newline at end of file +await import("./electron.js") diff --git a/electron.ts b/electron.ts index 6ebf9a9cf..188240610 100644 --- a/electron.ts +++ b/electron.ts @@ -48,11 +48,11 @@ electron.app.on("ready", async () => { await windowService.createMainWindow(electron.app); if (process.platform === "darwin") { - electron.app.on("activate", async () => { + electron.app.on("activate", async () => { if (electron.BrowserWindow.getAllWindows().length === 0) { - await windowService.createMainWindow(electron.app); + await windowService.createMainWindow(electron.app); } - }); + }); } tray.createTray(); diff --git a/integration-tests/auth.setup.ts b/integration-tests/auth.setup.ts index ed27ca648..8a1d4e4d1 100644 --- a/integration-tests/auth.setup.ts +++ b/integration-tests/auth.setup.ts @@ -5,7 +5,7 @@ const authFile = 'playwright/.auth/user.json'; const ROOT_URL = "http://localhost:8082"; const LOGIN_PASSWORD = "demo1234"; -// Reference: https://playwright.dev/docs/auth#basic-shared-account-in-all-tests +// Reference: https://playwright.dev/docs/auth#basic-shared-account-in-all-tests setup("authenticate", async ({ page }) => { await page.goto(ROOT_URL); @@ -13,5 +13,5 @@ setup("authenticate", async ({ page }) => { await page.getByRole("textbox", { name: "Password" }).fill(LOGIN_PASSWORD); await page.getByRole("button", { name: "Login"}).click(); - await page.context().storageState({ path: authFile }); -}); \ No newline at end of file + await page.context().storageState({ path: authFile }); +}); diff --git a/integration-tests/duplicate.spec.ts b/integration-tests/duplicate.spec.ts index 7decbd7f0..e984c2f98 100644 --- a/integration-tests/duplicate.spec.ts +++ b/integration-tests/duplicate.spec.ts @@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test'; test("Can duplicate note with broken links", async ({ page }) => { await page.goto(`http://localhost:8082/#2VammGGdG6Ie`); await page.locator('.tree-wrapper .fancytree-active').getByText('Note map').click({ button: 'right' }); - await page.getByText('Duplicate subtree').click(); + await page.getByText('Duplicate subtree').click(); await expect(page.locator(".toast-body")).toBeHidden(); await expect(page.locator('.tree-wrapper').getByText('Note map (dup)')).toBeVisible(); -}); \ No newline at end of file +}); diff --git a/integration-tests/help.spec.ts b/integration-tests/help.spec.ts index 4025b6e09..cd7722222 100644 --- a/integration-tests/help.spec.ts +++ b/integration-tests/help.spec.ts @@ -18,6 +18,6 @@ test('Complete help in search', async ({ page }) => { await page.locator('#launcher-container').getByRole('button', { name: '' }).first().click(); await page.getByRole('cell', { name: ' ' }).locator('span').first().click(); - await page.getByRole('button', { name: 'complete help on search syntax' }).click(); + await page.getByRole('button', { name: 'complete help on search syntax' }).click(); expect((await page.waitForEvent('popup')).url()).toBe("https://triliumnext.github.io/Docs/Wiki/search.html"); -}); \ No newline at end of file +}); diff --git a/integration-tests/i18n.spec.ts b/integration-tests/i18n.spec.ts index 0e700ad47..bd935ac68 100644 --- a/integration-tests/i18n.spec.ts +++ b/integration-tests/i18n.spec.ts @@ -13,7 +13,7 @@ test("User can change language from settings", async ({ page }) => { await page.locator('#center-pane').getByText('Appearance').click(); // Check that the default value (English) is set. - await expect(page.locator('#center-pane')).toContainText('Theme'); + await expect(page.locator('#center-pane')).toContainText('Theme'); const languageCombobox = await page.getByRole('combobox').first(); await expect(languageCombobox).toHaveValue("en"); @@ -25,7 +25,7 @@ test("User can change language from settings", async ({ page }) => { languageCombobox.selectOption("en"); }); -test("Restores language on start-up on desktop", async ({ page, context }) => { +test("Restores language on start-up on desktop", async ({ page, context }) => { await page.goto('http://localhost:8082'); await expect(page.locator('#launcher-pane').first()).toContainText("Open New Window"); }); @@ -40,4 +40,4 @@ test("Restores language on start-up on mobile", async ({ page, context }) => { ]); await page.goto('http://localhost:8082'); await expect(page.locator('#launcher-pane div').first()).toContainText("Open New Window"); -}); \ No newline at end of file +}); diff --git a/integration-tests/katex.disabled.ts b/integration-tests/katex.disabled.ts index c1ce0d9d7..0e99a3fbe 100644 --- a/integration-tests/katex.disabled.ts +++ b/integration-tests/katex.disabled.ts @@ -9,10 +9,10 @@ test("Can insert equations", async ({ page }) => { // Create a new note // await page.locator("button.button-widget.bx-file-blank") - // .click(); + // .click(); const activeNote = page.locator(".component.note-split:visible"); const noteContent = activeNote .locator(".note-detail-editable-text-editor") await noteContent.press("Ctrl+M"); -}); \ No newline at end of file +}); diff --git a/integration-tests/settings.spec.ts b/integration-tests/settings.spec.ts index ab9faf57f..835e8e341 100644 --- a/integration-tests/settings.spec.ts +++ b/integration-tests/settings.spec.ts @@ -18,4 +18,4 @@ test("Spellcheck settings not displayed on web", async ({ page }) => { await expect(page.getByRole('heading', { name: 'Tray' })).toBeHidden(); await expect(page.getByText('These options apply only for desktop builds')).toBeVisible(); await expect(page.getByText('Enable spellcheck')).toBeHidden(); -}); \ No newline at end of file +}); diff --git a/integration-tests/tree.spec.ts b/integration-tests/tree.spec.ts index 38482eeb0..0bc9caf22 100644 --- a/integration-tests/tree.spec.ts +++ b/integration-tests/tree.spec.ts @@ -15,4 +15,4 @@ test("Renders on mobile", async ({ page, context }) => { ]); await page.goto('http://localhost:8082'); await expect(page.locator('.tree')).toContainText('Trilium Integration Test'); -}); \ No newline at end of file +}); diff --git a/integration-tests/update_check.spec.ts b/integration-tests/update_check.spec.ts index 207395296..732cd501a 100644 --- a/integration-tests/update_check.spec.ts +++ b/integration-tests/update_check.spec.ts @@ -9,4 +9,4 @@ test("Displays update badge when there is a version available", async ({ page }) const page1 = await page.waitForEvent('popup'); expect(page1.url()).toBe(`https://github.com/TriliumNext/Notes/releases/tag/v${expectedVersion}`); -}); \ No newline at end of file +}); diff --git a/loader-register.js b/loader-register.js index 346caacb5..b35f8bd59 100644 --- a/loader-register.js +++ b/loader-register.js @@ -7,4 +7,4 @@ import { register } from 'node:module'; import { pathToFileURL } from 'node:url'; -register('ts-node/esm', pathToFileURL('./')); \ No newline at end of file +register('ts-node/esm', pathToFileURL('./')); diff --git a/playwright.config.ts b/playwright.config.ts index 313735158..ca8b95889 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -42,17 +42,17 @@ export default defineConfig({ /* Configure projects for major browsers */ projects: [ { - name: "setup", - testMatch: /.*\.setup\.ts/ + name: "setup", + testMatch: /.*\.setup\.ts/ }, { - name: "firefox", - use: { + name: "firefox", + use: { ...devices[ "Desktop Firefox" ], storageState: "playwright/.auth/user.json" - }, - dependencies: [ "setup" ] + }, + dependencies: [ "setup" ] }, /* Test against mobile viewports. */ diff --git a/spec/etapi/import.ts b/spec/etapi/import.ts index 942a9871f..d800075d4 100644 --- a/spec/etapi/import.ts +++ b/spec/etapi/import.ts @@ -9,12 +9,12 @@ etapi.describeEtapi("import", () => { const scriptDir = path.dirname(fileURLToPath(import.meta.url)); const zipFileBuffer = fs.readFileSync( - path.resolve(scriptDir, "test-export.zip") + path.resolve(scriptDir, "test-export.zip") ); const response = await etapi.postEtapiContent( - "notes/root/import", - zipFileBuffer + "notes/root/import", + zipFileBuffer ); expect(response.status).toEqual(201); @@ -24,7 +24,7 @@ etapi.describeEtapi("import", () => { expect(branch.parentNoteId).toEqual("root"); const content = await ( - await etapi.getEtapiContent(`notes/${note.noteId}/content`) + await etapi.getEtapiContent(`notes/${note.noteId}/content`) ).text(); expect(content).toContain("test export content"); }); diff --git a/spec/etapi/notes.ts b/spec/etapi/notes.ts index cbfd4de7e..3db54ab1b 100644 --- a/spec/etapi/notes.ts +++ b/spec/etapi/notes.ts @@ -4,11 +4,11 @@ import etapi from "../support/etapi.js"; etapi.describeEtapi("notes", () => { it("create", async () => { const { note, branch } = await etapi.postEtapi("create-note", { - parentNoteId: "root", - type: "text", - title: "Hello World!", - content: "Content", - prefix: "Custom prefix", + parentNoteId: "root", + type: "text", + title: "Hello World!", + content: "Content", + prefix: "Custom prefix", }); expect(note.title).toEqual("Hello World!"); @@ -19,7 +19,7 @@ etapi.describeEtapi("notes", () => { expect(rNote.title).toEqual("Hello World!"); const rContent = await ( - await etapi.getEtapiContent(`notes/${note.noteId}/content`) + await etapi.getEtapiContent(`notes/${note.noteId}/content`) ).text(); expect(rContent).toEqual("Content"); @@ -30,18 +30,18 @@ etapi.describeEtapi("notes", () => { it("patch", async () => { const { note } = await etapi.postEtapi("create-note", { - parentNoteId: "root", - type: "text", - title: "Hello World!", - content: "Content", + parentNoteId: "root", + type: "text", + title: "Hello World!", + content: "Content", }); await etapi.patchEtapi(`notes/${note.noteId}`, { - title: "new title", - type: "code", - mime: "text/apl", - dateCreated: "2000-01-01 12:34:56.999+0200", - utcDateCreated: "2000-01-01 10:34:56.999Z", + title: "new title", + type: "code", + mime: "text/apl", + dateCreated: "2000-01-01 12:34:56.999+0200", + utcDateCreated: "2000-01-01 10:34:56.999Z", }); const rNote = await etapi.getEtapi(`notes/${note.noteId}`); @@ -54,26 +54,26 @@ etapi.describeEtapi("notes", () => { it("update content", async () => { const { note } = await etapi.postEtapi("create-note", { - parentNoteId: "root", - type: "text", - title: "Hello World!", - content: "Content", + parentNoteId: "root", + type: "text", + title: "Hello World!", + content: "Content", }); await etapi.putEtapiContent(`notes/${note.noteId}/content`, "new content"); const rContent = await ( - await etapi.getEtapiContent(`notes/${note.noteId}/content`) + await etapi.getEtapiContent(`notes/${note.noteId}/content`) ).text(); expect(rContent).toEqual("new content"); }); it("create / update binary content", async () => { const { note } = await etapi.postEtapi("create-note", { - parentNoteId: "root", - type: "file", - title: "Hello World!", - content: "ZZZ", + parentNoteId: "root", + type: "file", + title: "Hello World!", + content: "ZZZ", }); const updatedContent = crypto.randomBytes(16); @@ -81,17 +81,17 @@ etapi.describeEtapi("notes", () => { await etapi.putEtapiContent(`notes/${note.noteId}/content`, updatedContent); const rContent = await ( - await etapi.getEtapiContent(`notes/${note.noteId}/content`) + await etapi.getEtapiContent(`notes/${note.noteId}/content`) ).arrayBuffer(); expect(Buffer.from(new Uint8Array(rContent))).toEqual(updatedContent); }); it("delete note", async () => { const { note } = await etapi.postEtapi("create-note", { - parentNoteId: "root", - type: "text", - title: "Hello World!", - content: "Content", + parentNoteId: "root", + type: "text", + title: "Hello World!", + content: "Content", }); await etapi.deleteEtapi(`notes/${note.noteId}`); diff --git a/spec/search/becca_mocking.ts b/spec/search/becca_mocking.ts index eaae2741f..33bbb63bb 100644 --- a/spec/search/becca_mocking.ts +++ b/spec/search/becca_mocking.ts @@ -24,12 +24,12 @@ class NoteBuilder { label(name: string, value = "", isInheritable = false) { new BAttribute({ - attributeId: id(), - noteId: this.note.noteId, - type: "label", - isInheritable, - name, - value, + attributeId: id(), + noteId: this.note.noteId, + type: "label", + isInheritable, + name, + value, }); return this; @@ -37,11 +37,11 @@ class NoteBuilder { relation(name: string, targetNote: BNote) { new BAttribute({ - attributeId: id(), - noteId: this.note.noteId, - type: "relation", - name, - value: targetNote.noteId, + attributeId: id(), + noteId: this.note.noteId, + type: "relation", + name, + value: targetNote.noteId, }); return this; @@ -49,11 +49,11 @@ class NoteBuilder { child(childNoteBuilder: NoteBuilder, prefix = "") { new BBranch({ - branchId: id(), - noteId: childNoteBuilder.note.noteId, - parentNoteId: this.note.noteId, - prefix, - notePosition: 10, + branchId: id(), + noteId: childNoteBuilder.note.noteId, + parentNoteId: this.note.noteId, + prefix, + notePosition: 10, }); return this; @@ -67,10 +67,10 @@ function id() { function note(title: string, extraParams = {}) { const row = Object.assign( { - noteId: id(), - title: title, - type: "text" as NoteType, - mime: "text/html", + noteId: id(), + title: title, + type: "text" as NoteType, + mime: "text/html", }, extraParams ); diff --git a/spec/search/lexer.spec.ts b/spec/search/lexer.spec.ts index d97f435c3..2e3179fa3 100644 --- a/spec/search/lexer.spec.ts +++ b/spec/search/lexer.spec.ts @@ -3,65 +3,65 @@ import lex from "../../src/services/search/services/lex.js"; describe("Lexer fulltext", () => { it("simple lexing", () => { expect(lex("hello world").fulltextTokens.map((t) => t.token)).toEqual([ - "hello", - "world", + "hello", + "world", ]); expect(lex("hello, world").fulltextTokens.map((t) => t.token)).toEqual([ - "hello", - "world", + "hello", + "world", ]); }); it("use quotes to keep words together", () => { expect( - lex("'hello world' my friend").fulltextTokens.map((t) => t.token) + lex("'hello world' my friend").fulltextTokens.map((t) => t.token) ).toEqual(["hello world", "my", "friend"]); expect( - lex('"hello world" my friend').fulltextTokens.map((t) => t.token) + lex('"hello world" my friend').fulltextTokens.map((t) => t.token) ).toEqual(["hello world", "my", "friend"]); expect( - lex("`hello world` my friend").fulltextTokens.map((t) => t.token) + lex("`hello world` my friend").fulltextTokens.map((t) => t.token) ).toEqual(["hello world", "my", "friend"]); }); it("you can use different quotes and other special characters inside quotes", () => { expect( - lex("'i can use \" or ` or #~=*' without problem").fulltextTokens.map( + lex("'i can use \" or ` or #~=*' without problem").fulltextTokens.map( (t) => t.token - ) + ) ).toEqual(['i can use " or ` or #~=*', "without", "problem"]); }); it("I can use backslash to escape quotes", () => { expect(lex('hello \\"world\\"').fulltextTokens.map((t) => t.token)).toEqual( - ["hello", '"world"'] + ["hello", '"world"'] ); expect(lex("hello \\'world\\'").fulltextTokens.map((t) => t.token)).toEqual( - ["hello", "'world'"] + ["hello", "'world'"] ); expect(lex("hello \\`world\\`").fulltextTokens.map((t) => t.token)).toEqual( - ["hello", "`world`"] + ["hello", "`world`"] ); expect( - lex('"hello \\"world\\"').fulltextTokens.map((t) => t.token) + lex('"hello \\"world\\"').fulltextTokens.map((t) => t.token) ).toEqual(['hello "world"']); expect( - lex("'hello \\'world\\''").fulltextTokens.map((t) => t.token) + lex("'hello \\'world\\''").fulltextTokens.map((t) => t.token) ).toEqual(["hello 'world'"]); expect( - lex("`hello \\`world\\``").fulltextTokens.map((t) => t.token) + lex("`hello \\`world\\``").fulltextTokens.map((t) => t.token) ).toEqual(["hello `world`"]); expect(lex("\\#token").fulltextTokens.map((t) => t.token)).toEqual([ - "#token", + "#token", ]); }); @@ -69,40 +69,40 @@ describe("Lexer fulltext", () => { const lexResult = lex("d'Artagnan is dead #hero = d'Artagnan"); expect(lexResult.fulltextTokens.map((t) => t.token)).toEqual([ - "d'artagnan", - "is", - "dead", + "d'artagnan", + "is", + "dead", ]); expect(lexResult.expressionTokens.map((t) => t.token)).toEqual([ - "#hero", - "=", - "d'artagnan", + "#hero", + "=", + "d'artagnan", ]); }); it("if quote is not ended then it's just one long token", () => { expect(lex("'unfinished quote").fulltextTokens.map((t) => t.token)).toEqual( - ["unfinished quote"] + ["unfinished quote"] ); }); it("parenthesis and symbols in fulltext section are just normal characters", () => { expect( - lex("what's u=p ").fulltextTokens.map((t) => t.token) + lex("what's u=p ").fulltextTokens.map((t) => t.token) ).toEqual(["what's", "u=p", ""]); }); it("operator characters in expressions are separate tokens", () => { expect( - lex("# abc+=-def**-+d").expressionTokens.map((t) => t.token) + lex("# abc+=-def**-+d").expressionTokens.map((t) => t.token) ).toEqual(["#", "abc", "+=-", "def", "**-+", "d"]); }); it("escaping special characters", () => { expect(lex("hello \\#\\~\\'").fulltextTokens.map((t) => t.token)).toEqual([ - "hello", - "#~'", + "hello", + "#~'", ]); }); }); @@ -110,132 +110,132 @@ describe("Lexer fulltext", () => { describe("Lexer expression", () => { it("simple attribute existence", () => { expect( - lex("#label ~relation").expressionTokens.map((t) => t.token) + lex("#label ~relation").expressionTokens.map((t) => t.token) ).toEqual(["#label", "~relation"]); }); it("simple label operators", () => { expect(lex("#label*=*text").expressionTokens.map((t) => t.token)).toEqual([ - "#label", - "*=*", - "text", + "#label", + "*=*", + "text", ]); }); it("simple label operator with in quotes", () => { expect(lex("#label*=*'text'").expressionTokens).toEqual([ - { token: "#label", inQuotes: false, startIndex: 0, endIndex: 5 }, - { token: "*=*", inQuotes: false, startIndex: 6, endIndex: 8 }, - { token: "text", inQuotes: true, startIndex: 10, endIndex: 13 }, + { token: "#label", inQuotes: false, startIndex: 0, endIndex: 5 }, + { token: "*=*", inQuotes: false, startIndex: 6, endIndex: 8 }, + { token: "text", inQuotes: true, startIndex: 10, endIndex: 13 }, ]); }); it("simple label operator with param without quotes", () => { expect(lex("#label*=*text").expressionTokens).toEqual([ - { token: "#label", inQuotes: false, startIndex: 0, endIndex: 5 }, - { token: "*=*", inQuotes: false, startIndex: 6, endIndex: 8 }, - { token: "text", inQuotes: false, startIndex: 9, endIndex: 12 }, + { token: "#label", inQuotes: false, startIndex: 0, endIndex: 5 }, + { token: "*=*", inQuotes: false, startIndex: 6, endIndex: 8 }, + { token: "text", inQuotes: false, startIndex: 9, endIndex: 12 }, ]); }); it("simple label operator with empty string param", () => { expect(lex("#label = ''").expressionTokens).toEqual([ - { token: "#label", inQuotes: false, startIndex: 0, endIndex: 5 }, - { token: "=", inQuotes: false, startIndex: 7, endIndex: 7 }, - // weird case for empty strings which ends up with endIndex < startIndex :-( - { token: "", inQuotes: true, startIndex: 10, endIndex: 9 }, + { token: "#label", inQuotes: false, startIndex: 0, endIndex: 5 }, + { token: "=", inQuotes: false, startIndex: 7, endIndex: 7 }, + // weird case for empty strings which ends up with endIndex < startIndex :-( + { token: "", inQuotes: true, startIndex: 10, endIndex: 9 }, ]); }); it("note. prefix also separates fulltext from expression", () => { expect( - lex(`hello fulltext note.labels.capital = Prague`).expressionTokens.map( + lex(`hello fulltext note.labels.capital = Prague`).expressionTokens.map( (t) => t.token - ) + ) ).toEqual(["note", ".", "labels", ".", "capital", "=", "prague"]); }); it("note. prefix in quotes will note start expression", () => { expect( - lex(`hello fulltext "note.txt"`).expressionTokens.map((t) => t.token) + lex(`hello fulltext "note.txt"`).expressionTokens.map((t) => t.token) ).toEqual([]); expect( - lex(`hello fulltext "note.txt"`).fulltextTokens.map((t) => t.token) + lex(`hello fulltext "note.txt"`).fulltextTokens.map((t) => t.token) ).toEqual(["hello", "fulltext", "note.txt"]); }); it("complex expressions with and, or and parenthesis", () => { expect( - lex(`# (#label=text OR #second=text) AND ~relation`).expressionTokens.map( + lex(`# (#label=text OR #second=text) AND ~relation`).expressionTokens.map( (t) => t.token - ) + ) ).toEqual([ - "#", - "(", - "#label", - "=", - "text", - "or", - "#second", - "=", - "text", - ")", - "and", - "~relation", + "#", + "(", + "#label", + "=", + "text", + "or", + "#second", + "=", + "text", + ")", + "and", + "~relation", ]); }); it("dot separated properties", () => { expect( - lex( + lex( `# ~author.title = 'Hugh Howey' AND note.'book title' = 'Silo'` - ).expressionTokens.map((t) => t.token) + ).expressionTokens.map((t) => t.token) ).toEqual([ - "#", - "~author", - ".", - "title", - "=", - "hugh howey", - "and", - "note", - ".", - "book title", - "=", - "silo", + "#", + "~author", + ".", + "title", + "=", + "hugh howey", + "and", + "note", + ".", + "book title", + "=", + "silo", ]); }); it("negation of label and relation", () => { expect( - lex(`#!capital ~!neighbor`).expressionTokens.map((t) => t.token) + lex(`#!capital ~!neighbor`).expressionTokens.map((t) => t.token) ).toEqual(["#!capital", "~!neighbor"]); }); it("negation of sub-expression", () => { expect( - lex(`# not(#capital) and note.noteId != "root"`).expressionTokens.map( + lex(`# not(#capital) and note.noteId != "root"`).expressionTokens.map( (t) => t.token - ) + ) ).toEqual([ - "#", - "not", - "(", - "#capital", - ")", - "and", - "note", - ".", - "noteid", - "!=", - "root", + "#", + "not", + "(", + "#capital", + ")", + "and", + "note", + ".", + "noteid", + "!=", + "root", ]); }); it("order by multiple labels", () => { expect(lex(`# orderby #a,#b`).expressionTokens.map((t) => t.token)).toEqual( - ["#", "orderby", "#a", ",", "#b"] + ["#", "orderby", "#a", ",", "#b"] ); }); }); @@ -243,14 +243,14 @@ describe("Lexer expression", () => { describe("Lexer invalid queries and edge cases", () => { it("concatenated attributes", () => { expect(lex("#label~relation").expressionTokens.map((t) => t.token)).toEqual( - ["#label", "~relation"] + ["#label", "~relation"] ); }); it("trailing escape \\", () => { expect(lex("abc \\").fulltextTokens.map((t) => t.token)).toEqual([ - "abc", - "\\", + "abc", + "\\", ]); }); }); diff --git a/spec/services/export/md.spec.ts b/spec/services/export/md.spec.ts index f9a4e4ab1..b9e32ef86 100644 --- a/spec/services/export/md.spec.ts +++ b/spec/services/export/md.spec.ts @@ -49,4 +49,4 @@ describe("Markdown export", () => { expect(markdownExportService.toMarkdown(html)).toBe(expected); }); -}); \ No newline at end of file +}); diff --git a/spec/support/etapi.ts b/spec/support/etapi.ts index 19043d8fa..3a2b362cf 100644 --- a/spec/support/etapi.ts +++ b/spec/support/etapi.ts @@ -19,11 +19,11 @@ function describeEtapi( let appProcess: ReturnType; beforeAll(async () => { - + }); afterAll(() => { - + }); specDefinitions(); @@ -34,7 +34,7 @@ async function getEtapiResponse(url: string): Promise { return await fetch(`${HOST}/etapi/${url}`, { method: "GET", headers: { - Authorization: getEtapiAuthorizationHeader(), + Authorization: getEtapiAuthorizationHeader(), }, }); } @@ -48,7 +48,7 @@ async function getEtapiContent(url: string): Promise { const response = await fetch(`${HOST}/etapi/${url}`, { method: "GET", headers: { - Authorization: getEtapiAuthorizationHeader(), + Authorization: getEtapiAuthorizationHeader(), }, }); @@ -64,8 +64,8 @@ async function postEtapi( const response = await fetch(`${HOST}/etapi/${url}`, { method: "POST", headers: { - "Content-Type": "application/json", - Authorization: getEtapiAuthorizationHeader(), + "Content-Type": "application/json", + Authorization: getEtapiAuthorizationHeader(), }, body: JSON.stringify(data), }); @@ -79,8 +79,8 @@ async function postEtapiContent( const response = await fetch(`${HOST}/etapi/${url}`, { method: "POST", headers: { - "Content-Type": "application/octet-stream", - Authorization: getEtapiAuthorizationHeader(), + "Content-Type": "application/octet-stream", + Authorization: getEtapiAuthorizationHeader(), }, body: data, }); @@ -97,8 +97,8 @@ async function putEtapi( const response = await fetch(`${HOST}/etapi/${url}`, { method: "PUT", headers: { - "Content-Type": "application/json", - Authorization: getEtapiAuthorizationHeader(), + "Content-Type": "application/json", + Authorization: getEtapiAuthorizationHeader(), }, body: JSON.stringify(data), }); @@ -112,8 +112,8 @@ async function putEtapiContent( const response = await fetch(`${HOST}/etapi/${url}`, { method: "PUT", headers: { - "Content-Type": "application/octet-stream", - Authorization: getEtapiAuthorizationHeader(), + "Content-Type": "application/octet-stream", + Authorization: getEtapiAuthorizationHeader(), }, body: data, }); @@ -130,8 +130,8 @@ async function patchEtapi( const response = await fetch(`${HOST}/etapi/${url}`, { method: "PATCH", headers: { - "Content-Type": "application/json", - Authorization: getEtapiAuthorizationHeader(), + "Content-Type": "application/json", + Authorization: getEtapiAuthorizationHeader(), }, body: JSON.stringify(data), }); @@ -142,7 +142,7 @@ async function deleteEtapi(url: string): Promise { const response = await fetch(`${HOST}/etapi/${url}`, { method: "DELETE", headers: { - Authorization: getEtapiAuthorizationHeader(), + Authorization: getEtapiAuthorizationHeader(), }, }); return await processEtapiResponse(response); diff --git a/spec/support/utils.spec.ts b/spec/support/utils.spec.ts index 3c3b2ce80..c385d0729 100644 --- a/spec/support/utils.spec.ts +++ b/spec/support/utils.spec.ts @@ -11,4 +11,4 @@ Hello world 123`); }); -}); \ No newline at end of file +}); diff --git a/spec/support/utils.ts b/spec/support/utils.ts index 849cfe840..1e38fbe84 100644 --- a/spec/support/utils.ts +++ b/spec/support/utils.ts @@ -1,4 +1,4 @@ -export function trimIndentation(strings: TemplateStringsArray) { +export function trimIndentation(strings: TemplateStringsArray) { const str = strings.toString(); // Count the number of spaces on the first line. @@ -6,10 +6,10 @@ export function trimIndentation(strings: TemplateStringsArray) { while (str.charAt(numSpaces) == ' ' && numSpaces < str.length) { numSpaces++; } - - // Trim the indentation of the first line in all the lines. + + // Trim the indentation of the first line in all the lines. const lines = str.split("\n"); - const output = []; + const output = []; for (let i=0; i(query, [attachmentId]) @@ -279,7 +279,7 @@ export default class Becca { /** * This interface contains the data that is shared across all the objects of a given derived class of {@link AbstractBeccaEntity}. - * For example, all BAttributes will share their content, but all BBranches will have another set of this data. + * For example, all BAttributes will share their content, but all BBranches will have another set of this data. */ export interface ConstructorData> { primaryKeyName: string; @@ -299,4 +299,4 @@ export interface NotePojo { dateModified?: string; utcDateCreated: string; utcDateModified?: string; -} \ No newline at end of file +} diff --git a/src/becca/entities/abstract_becca_entity.ts b/src/becca/entities/abstract_becca_entity.ts index cd169c0b2..f86eced67 100644 --- a/src/becca/entities/abstract_becca_entity.ts +++ b/src/becca/entities/abstract_becca_entity.ts @@ -19,7 +19,7 @@ interface ContentOpts { /** * Base class for all backend entities. - * + * * @type T the same entity type needed for self-reference in {@link ConstructorData}. */ abstract class AbstractBeccaEntity> { @@ -27,7 +27,7 @@ abstract class AbstractBeccaEntity> { utcDateModified?: string; dateCreated?: string; dateModified?: string; - + utcDateCreated!: string; isProtected?: boolean; @@ -99,15 +99,15 @@ abstract class AbstractBeccaEntity> { } /** - * Saves entity - executes SQL, but doesn't commit the transaction on its own - */ + * Saves entity - executes SQL, but doesn't commit the transaction on its own + */ save(opts?: {}): this { const constructorData = (this.constructor as unknown as ConstructorData); const entityName = constructorData.entityName; const primaryKeyName = constructorData.primaryKeyName; const isNewEntity = !(this as any)[primaryKeyName]; - + this.beforeSaving(opts); const pojo = this.getPojoToSave(); @@ -160,7 +160,7 @@ abstract class AbstractBeccaEntity> { if (protectedSessionService.isProtectedSessionAvailable()) { const encryptedContent = protectedSessionService.encrypt(content); if (!encryptedContent) { - throw new Error(`Unable to encrypt the content of the entity.`); + throw new Error(`Unable to encrypt the content of the entity.`); } content = encryptedContent; } else { @@ -216,11 +216,11 @@ abstract class AbstractBeccaEntity> { private saveBlob(content: string | Buffer, unencryptedContentForHashCalculation: string | Buffer, opts: ContentOpts = {}) { /* - * We're using the unencrypted blob for the hash calculation, because otherwise the random IV would - * cause every content blob to be unique which would balloon the database size (esp. with revisioning). - * This has minor security implications (it's easy to infer that given content is shared between different - * notes/attachments), but the trade-off comes out clearly positive. - */ + * We're using the unencrypted blob for the hash calculation, because otherwise the random IV would + * cause every content blob to be unique which would balloon the database size (esp. with revisioning). + * This has minor security implications (it's easy to infer that given content is shared between different + * notes/attachments), but the trade-off comes out clearly positive. + */ const newBlobId = utils.hashedBlobId(unencryptedContentForHashCalculation); const blobNeedsInsert = !sql.getValue('SELECT 1 FROM blobs WHERE blobId = ?', [newBlobId]); @@ -261,7 +261,7 @@ abstract class AbstractBeccaEntity> { return newBlobId; } - protected _getContent(): string | Buffer { + protected _getContent(): string | Buffer { const row = sql.getRow<{ content: string | Buffer }>(`SELECT content FROM blobs WHERE blobId = ?`, [this.blobId]); if (!row) { @@ -273,10 +273,10 @@ abstract class AbstractBeccaEntity> { } /** - * Mark the entity as (soft) deleted. It will be completely erased later. - * - * This is a low-level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead. - */ + * Mark the entity as (soft) deleted. It will be completely erased later. + * + * This is a low-level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead. + */ markAsDeleted(deleteId: string | null = null) { const constructorData = (this.constructor as unknown as ConstructorData); const entityId = (this as any)[constructorData.primaryKeyName]; @@ -285,7 +285,7 @@ abstract class AbstractBeccaEntity> { this.utcDateModified = dateUtils.utcNowDateTime(); sql.execute(`UPDATE ${entityName} SET isDeleted = 1, deleteId = ?, utcDateModified = ? - WHERE ${constructorData.primaryKeyName} = ?`, + WHERE ${constructorData.primaryKeyName} = ?`, [deleteId, this.utcDateModified, entityId]); if (this.dateModified) { @@ -310,7 +310,7 @@ abstract class AbstractBeccaEntity> { this.utcDateModified = dateUtils.utcNowDateTime(); sql.execute(`UPDATE ${entityName} SET isDeleted = 1, utcDateModified = ? - WHERE ${constructorData.primaryKeyName} = ?`, + WHERE ${constructorData.primaryKeyName} = ?`, [this.utcDateModified, entityId]); log.info(`Marking ${entityName} ${entityId} as deleted`); diff --git a/src/becca/entities/battachment.ts b/src/becca/entities/battachment.ts index c6b8d7076..b26599598 100644 --- a/src/becca/entities/battachment.ts +++ b/src/becca/entities/battachment.ts @@ -170,7 +170,7 @@ class BAttachment extends AbstractBeccaEntity { if (this.role === 'image' && parentNote.type === 'text') { const origContent = parentNote.getContent(); - + if (typeof origContent !== "string") { throw new Error(`Note with ID '${note.noteId} has a text type but non-string content.`); } @@ -201,8 +201,8 @@ class BAttachment extends AbstractBeccaEntity { if (this.position === undefined || this.position === null) { this.position = 10 + sql.getValue(`SELECT COALESCE(MAX(position), 0) - FROM attachments - WHERE ownerId = ?`, [this.noteId]); + FROM attachments + WHERE ownerId = ?`, [this.noteId]); } this.dateModified = dateUtils.localNowDateTime(); diff --git a/src/becca/entities/bbranch.ts b/src/becca/entities/bbranch.ts index daa71f917..674136c73 100644 --- a/src/becca/entities/bbranch.ts +++ b/src/becca/entities/bbranch.ts @@ -87,7 +87,7 @@ class BBranch extends AbstractBeccaEntity { if (!childNote.parents.includes(parentNote)) { childNote.parents.push(parentNote); } - + if (!parentNote.children.includes(childNote)) { parentNote.children.push(childNote); } @@ -122,23 +122,23 @@ class BBranch extends AbstractBeccaEntity { } /** - * Branch is weak when its existence should not hinder deletion of its note. - * As a result, note with only weak branches should be immediately deleted. - * An example is shared or bookmarked clones - they are created automatically and exist for technical reasons, - * not as user-intended actions. From user perspective, they don't count as real clones and for the purpose - * of deletion should not act as a clone. - */ + * Branch is weak when its existence should not hinder deletion of its note. + * As a result, note with only weak branches should be immediately deleted. + * An example is shared or bookmarked clones - they are created automatically and exist for technical reasons, + * not as user-intended actions. From user perspective, they don't count as real clones and for the purpose + * of deletion should not act as a clone. + */ get isWeak() { return ['_share', '_lbBookmarks'].includes(this.parentNoteId); } /** - * Delete a branch. If this is a last note's branch, delete the note as well. - * - * @param deleteId - optional delete identified - * - * @returns true if note has been deleted, false otherwise - */ + * Delete a branch. If this is a last note's branch, delete the note as well. + * + * @param deleteId - optional delete identified + * + * @returns true if note has been deleted, false otherwise + */ deleteBranch(deleteId?: string, taskContext?: TaskContext): boolean { if (!deleteId) { deleteId = utils.randomString(10); diff --git a/src/becca/entities/bnote.ts b/src/becca/entities/bnote.ts index e2d0299dc..44d0686a6 100644 --- a/src/becca/entities/bnote.ts +++ b/src/becca/entities/bnote.ts @@ -178,15 +178,15 @@ class BNote extends AbstractBeccaEntity { } /** - * Returns strong (as opposed to weak) parent branches. See isWeak for details. - */ + * Returns strong (as opposed to weak) parent branches. See isWeak for details. + */ getStrongParentBranches() { return this.getParentBranches().filter(branch => !branch.isWeak); } /** - * @deprecated use getParentBranches() instead - */ + * @deprecated use getParentBranches() instead + */ getBranches() { return this.parentBranches; } @@ -209,20 +209,20 @@ class BNote extends AbstractBeccaEntity { } /** - * Note content has quite special handling - it's not a separate entity, but a lazily loaded - * part of Note entity with its own sync. Reasons behind this hybrid design has been: - * - * - content can be quite large, and it's not necessary to load it / fill memory for any note access even if we don't need a content, especially for bulk operations like search - * - changes in the note metadata or title should not trigger note content sync (so we keep separate utcDateModified and entity changes records) - * - but to the user note content and title changes are one and the same - single dateModified (so all changes must go through Note and content is not a separate entity) - */ + * Note content has quite special handling - it's not a separate entity, but a lazily loaded + * part of Note entity with its own sync. Reasons behind this hybrid design has been: + * + * - content can be quite large, and it's not necessary to load it / fill memory for any note access even if we don't need a content, especially for bulk operations like search + * - changes in the note metadata or title should not trigger note content sync (so we keep separate utcDateModified and entity changes records) + * - but to the user note content and title changes are one and the same - single dateModified (so all changes must go through Note and content is not a separate entity) + */ getContent() { return this._getContent(); } /** - * @throws Error in case of invalid JSON - */ + * @throws Error in case of invalid JSON + */ getJsonContent(): any | null { const content = this.getContent(); @@ -327,13 +327,13 @@ class BNote extends AbstractBeccaEntity { } /** - * Beware that the method must not create a copy of the array, but actually returns its internal array - * (for performance reasons) - * - * @param type - (optional) attribute type to filter - * @param name - (optional) attribute name to filter - * @returns all note's attributes, including inherited ones - */ + * Beware that the method must not create a copy of the array, but actually returns its internal array + * (for performance reasons) + * + * @param type - (optional) attribute type to filter + * @param name - (optional) attribute name to filter + * @returns all note's attributes, including inherited ones + */ getAttributes(type?: string, name?: string): BAttribute[] { this.__validateTypeName(type, name); this.__ensureAttributeCacheIsAvailable(); @@ -468,18 +468,18 @@ class BNote extends AbstractBeccaEntity { } /** - * @param name - label name - * @param value - label value - * @returns true if label exists (including inherited) - */ + * @param name - label name + * @param value - label value + * @returns true if label exists (including inherited) + */ hasLabel(name: string, value?: string): boolean { return this.hasAttribute(LABEL, name, value); } /** - * @param name - label name - * @returns true if label exists (including inherited) and does not have "false" value. - */ + * @param name - label name + * @returns true if label exists (including inherited) and does not have "false" value. + */ isLabelTruthy(name: string): boolean { const label = this.getLabel(name); @@ -491,112 +491,112 @@ class BNote extends AbstractBeccaEntity { } /** - * @param name - label name - * @param value - label value - * @returns true if label exists (excluding inherited) - */ + * @param name - label name + * @param value - label value + * @returns true if label exists (excluding inherited) + */ hasOwnedLabel(name: string, value?: string): boolean { return this.hasOwnedAttribute(LABEL, name, value); } /** - * @param name - relation name - * @param value - relation value - * @returns true if relation exists (including inherited) - */ + * @param name - relation name + * @param value - relation value + * @returns true if relation exists (including inherited) + */ hasRelation(name: string, value?: string): boolean { return this.hasAttribute(RELATION, name, value); } /** - * @param name - relation name - * @param value - relation value - * @returns true if relation exists (excluding inherited) - */ + * @param name - relation name + * @param value - relation value + * @returns true if relation exists (excluding inherited) + */ hasOwnedRelation(name: string, value?: string): boolean { return this.hasOwnedAttribute(RELATION, name, value); } /** - * @param name - label name - * @returns label if it exists, null otherwise - */ + * @param name - label name + * @returns label if it exists, null otherwise + */ getLabel(name: string): BAttribute | null { return this.getAttribute(LABEL, name); } /** - * @param name - label name - * @returns label if it exists, null otherwise - */ + * @param name - label name + * @returns label if it exists, null otherwise + */ getOwnedLabel(name: string): BAttribute | null { return this.getOwnedAttribute(LABEL, name); } /** - * @param name - relation name - * @returns relation if it exists, null otherwise - */ + * @param name - relation name + * @returns relation if it exists, null otherwise + */ getRelation(name: string): BAttribute | null { return this.getAttribute(RELATION, name); } /** - * @param name - relation name - * @returns relation if it exists, null otherwise - */ + * @param name - relation name + * @returns relation if it exists, null otherwise + */ getOwnedRelation(name: string): BAttribute | null { return this.getOwnedAttribute(RELATION, name); } /** - * @param name - label name - * @returns label value if label exists, null otherwise - */ + * @param name - label name + * @returns label value if label exists, null otherwise + */ getLabelValue(name: string): string | null { return this.getAttributeValue(LABEL, name); } /** - * @param name - label name - * @returns label value if label exists, null otherwise - */ + * @param name - label name + * @returns label value if label exists, null otherwise + */ getOwnedLabelValue(name: string): string | null { return this.getOwnedAttributeValue(LABEL, name); } /** - * @param name - relation name - * @returns relation value if relation exists, null otherwise - */ + * @param name - relation name + * @returns relation value if relation exists, null otherwise + */ getRelationValue(name: string): string | null { return this.getAttributeValue(RELATION, name); } /** - * @param name - relation name - * @returns relation value if relation exists, null otherwise - */ + * @param name - relation name + * @returns relation value if relation exists, null otherwise + */ getOwnedRelationValue(name: string): string | null { return this.getOwnedAttributeValue(RELATION, name); } /** - * @param attribute type (label, relation, etc.) - * @param name - attribute name - * @param value - attribute value - * @returns true if note has an attribute with given type and name (excluding inherited) - */ + * @param attribute type (label, relation, etc.) + * @param name - attribute name + * @param value - attribute value + * @returns true if note has an attribute with given type and name (excluding inherited) + */ hasOwnedAttribute(type: string, name: string, value?: string): boolean { return !!this.getOwnedAttribute(type, name, value); } /** - * @param type - attribute type (label, relation, etc.) - * @param name - attribute name - * @returns attribute of the given type and name. If there are more such attributes, first is returned. - * Returns null if there's no such attribute belonging to this note. - */ + * @param type - attribute type (label, relation, etc.) + * @param name - attribute name + * @returns attribute of the given type and name. If there are more such attributes, first is returned. + * Returns null if there's no such attribute belonging to this note. + */ getAttribute(type: string, name: string): BAttribute | null { const attributes = this.getAttributes(); @@ -604,10 +604,10 @@ class BNote extends AbstractBeccaEntity { } /** - * @param type - attribute type (label, relation, etc.) - * @param name - attribute name - * @returns attribute value of given type and name or null if no such attribute exists. - */ + * @param type - attribute type (label, relation, etc.) + * @param name - attribute name + * @returns attribute value of given type and name or null if no such attribute exists. + */ getAttributeValue(type: string, name: string): string | null { const attr = this.getAttribute(type, name); @@ -615,10 +615,10 @@ class BNote extends AbstractBeccaEntity { } /** - * @param type - attribute type (label, relation, etc.) - * @param name - attribute name - * @returns attribute value of given type and name or null if no such attribute exists. - */ + * @param type - attribute type (label, relation, etc.) + * @param name - attribute name + * @returns attribute value of given type and name or null if no such attribute exists. + */ getOwnedAttributeValue(type: string, name: string): string | null { const attr = this.getOwnedAttribute(type, name); @@ -626,62 +626,62 @@ class BNote extends AbstractBeccaEntity { } /** - * @param name - label name to filter - * @returns all note's labels (attributes with type label), including inherited ones - */ + * @param name - label name to filter + * @returns all note's labels (attributes with type label), including inherited ones + */ getLabels(name?: string): BAttribute[] { return this.getAttributes(LABEL, name); } /** - * @param name - label name to filter - * @returns all note's label values, including inherited ones - */ + * @param name - label name to filter + * @returns all note's label values, including inherited ones + */ getLabelValues(name: string): string[] { return this.getLabels(name).map(l => l.value); } /** - * @param name - label name to filter - * @returns all note's labels (attributes with type label), excluding inherited ones - */ + * @param name - label name to filter + * @returns all note's labels (attributes with type label), excluding inherited ones + */ getOwnedLabels(name: string): BAttribute[] { return this.getOwnedAttributes(LABEL, name); } /** - * @param name - label name to filter - * @returns all note's label values, excluding inherited ones - */ + * @param name - label name to filter + * @returns all note's label values, excluding inherited ones + */ getOwnedLabelValues(name: string): string[] { return this.getOwnedAttributes(LABEL, name).map(l => l.value); } /** - * @param name - relation name to filter - * @returns all note's relations (attributes with type relation), including inherited ones - */ + * @param name - relation name to filter + * @returns all note's relations (attributes with type relation), including inherited ones + */ getRelations(name?: string): BAttribute[] { return this.getAttributes(RELATION, name); } /** - * @param name - relation name to filter - * @returns all note's relations (attributes with type relation), excluding inherited ones - */ + * @param name - relation name to filter + * @returns all note's relations (attributes with type relation), excluding inherited ones + */ getOwnedRelations(name?: string | null): BAttribute[] { return this.getOwnedAttributes(RELATION, name); } /** - * Beware that the method must not create a copy of the array, but actually returns its internal array - * (for performance reasons) - * - * @param type - (optional) attribute type to filter - * @param name - (optional) attribute name to filter - * @param value - (optional) attribute value to filter - * @returns note's "owned" attributes - excluding inherited ones - */ + * Beware that the method must not create a copy of the array, but actually returns its internal array + * (for performance reasons) + * + * @param type - (optional) attribute type to filter + * @param name - (optional) attribute name to filter + * @param value - (optional) attribute value to filter + * @returns note's "owned" attributes - excluding inherited ones + */ getOwnedAttributes(type: string | null = null, name: string | null = null, value: string | null = null) { this.__validateTypeName(type, name); @@ -703,10 +703,10 @@ class BNote extends AbstractBeccaEntity { } /** - * @returns attribute belonging to this specific note (excludes inherited attributes) - * - * This method can be significantly faster than the getAttribute() - */ + * @returns attribute belonging to this specific note (excludes inherited attributes) + * + * This method can be significantly faster than the getAttribute() + */ getOwnedAttribute(type: string, name: string, value: string | null = null) { const attrs = this.getOwnedAttributes(type, name, value); @@ -776,12 +776,12 @@ class BNote extends AbstractBeccaEntity { } /** - * This is used for: - * - fast searching - * - note similarity evaluation - * - * @returns - returns flattened textual representation of note, prefixes and attributes - */ + * This is used for: + * - fast searching + * - note similarity evaluation + * + * @returns - returns flattened textual representation of note, prefixes and attributes + */ getFlatText() { if (!this.__flatTextCache) { this.__flatTextCache = `${this.noteId} ${this.type} ${this.mime} `; @@ -1077,7 +1077,7 @@ class BNote extends AbstractBeccaEntity { } /** @returns returns only notes which are templated, does not include their subtrees - * in effect returns notes which are influenced by note's non-inheritable attributes */ + * in effect returns notes which are influenced by note's non-inheritable attributes */ getInheritingNotes(): BNote[] { const arr: BNote[] = [this]; @@ -1120,10 +1120,10 @@ class BNote extends AbstractBeccaEntity { const query = opts.includeContentLength ? `SELECT attachments.*, LENGTH(blobs.content) AS contentLength - FROM attachments - JOIN blobs USING (blobId) - WHERE ownerId = ? AND isDeleted = 0 - ORDER BY position` + FROM attachments + JOIN blobs USING (blobId) + WHERE ownerId = ? AND isDeleted = 0 + ORDER BY position` : `SELECT * FROM attachments WHERE ownerId = ? AND isDeleted = 0 ORDER BY position`; return sql.getRows(query, [this.noteId]) @@ -1135,9 +1135,9 @@ class BNote extends AbstractBeccaEntity { const query = opts.includeContentLength ? `SELECT attachments.*, LENGTH(blobs.content) AS contentLength - FROM attachments - JOIN blobs USING (blobId) - WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0` + FROM attachments + JOIN blobs USING (blobId) + WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0` : `SELECT * FROM attachments WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0`; return sql.getRows(query, [this.noteId, attachmentId]) @@ -1147,10 +1147,10 @@ class BNote extends AbstractBeccaEntity { getAttachmentsByRole(role: string): BAttachment[] { return sql.getRows(` SELECT attachments.* - FROM attachments - WHERE ownerId = ? - AND role = ? - AND isDeleted = 0 + FROM attachments + WHERE ownerId = ? + AND role = ? + AND isDeleted = 0 ORDER BY position`, [this.noteId, role]) .map(row => new BAttachment(row)); } @@ -1161,10 +1161,10 @@ class BNote extends AbstractBeccaEntity { } /** - * Gives all possible note paths leading to this note. Paths containing search note are ignored (could form cycles) - * - * @returns array of notePaths (each represented by array of noteIds constituting the particular note path) - */ + * Gives all possible note paths leading to this note. Paths containing search note are ignored (could form cycles) + * + * @returns array of notePaths (each represented by array of noteIds constituting the particular note path) + */ getAllNotePaths(): string[][] { if (this.noteId === 'root') { return [['root']]; @@ -1209,19 +1209,19 @@ class BNote extends AbstractBeccaEntity { } /** - * Returns a note path considered to be the "best" - * - * @return array of noteIds constituting the particular note path - */ + * Returns a note path considered to be the "best" + * + * @return array of noteIds constituting the particular note path + */ getBestNotePath(hoistedNoteId: string = 'root'): string[] { return this.getSortedNotePathRecords(hoistedNoteId)[0]?.notePath; } /** - * Returns a note path considered to be the "best" - * - * @return serialized note path (e.g. 'root/a1h315/js725h') - */ + * Returns a note path considered to be the "best" + * + * @return serialized note path (e.g. 'root/a1h315/js725h') + */ getBestNotePathString(hoistedNoteId: string = 'root'): string { const notePath = this.getBestNotePath(hoistedNoteId); @@ -1229,8 +1229,8 @@ class BNote extends AbstractBeccaEntity { } /** - * @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree - */ + * @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree + */ isHiddenCompletely() { if (this.noteId === 'root') { return false; @@ -1250,8 +1250,8 @@ class BNote extends AbstractBeccaEntity { } /** - * @returns true if ancestorNoteId occurs in at least one of the note's paths - */ + * @returns true if ancestorNoteId occurs in at least one of the note's paths + */ isDescendantOfNote(ancestorNoteId: string): boolean { const notePaths = this.getAllNotePaths(); @@ -1259,12 +1259,12 @@ class BNote extends AbstractBeccaEntity { } /** - * Update's given attribute's value or creates it if it doesn't exist - * - * @param type - attribute type (label, relation, etc.) - * @param name - attribute name - * @param value - attribute value (optional) - */ + * Update's given attribute's value or creates it if it doesn't exist + * + * @param type - attribute type (label, relation, etc.) + * @param name - attribute name + * @param value - attribute value (optional) + */ setAttribute(type: AttributeType, name: string, value?: string) { const attributes = this.getOwnedAttributes(); const attr = attributes.find(attr => attr.type === type && attr.name === name); @@ -1288,12 +1288,12 @@ class BNote extends AbstractBeccaEntity { } /** - * Removes given attribute name-value pair if it exists. - * - * @param type - attribute type (label, relation, etc.) - * @param name - attribute name - * @param value - attribute value (optional) - */ + * Removes given attribute name-value pair if it exists. + * + * @param type - attribute type (label, relation, etc.) + * @param name - attribute name + * @param value - attribute value (optional) + */ removeAttribute(type: string, name: string, value?: string) { const attributes = this.getOwnedAttributes(); @@ -1305,13 +1305,13 @@ class BNote extends AbstractBeccaEntity { } /** - * Adds a new attribute to this note. The attribute is saved and returned. - * See addLabel, addRelation for more specific methods. - * - * @param type - attribute type (label / relation) - * @param name - name of the attribute, not including the leading ~/# - * @param value - value of the attribute - text for labels, target note ID for relations; optional. - */ + * Adds a new attribute to this note. The attribute is saved and returned. + * See addLabel, addRelation for more specific methods. + * + * @param type - attribute type (label / relation) + * @param name - name of the attribute, not including the leading ~/# + * @param value - value of the attribute - text for labels, target note ID for relations; optional. + */ addAttribute(type: AttributeType, name: string, value: string = "", isInheritable: boolean = false, position: number | null = null): BAttribute { return new BAttribute({ noteId: this.noteId, @@ -1324,33 +1324,33 @@ class BNote extends AbstractBeccaEntity { } /** - * Adds a new label to this note. The label attribute is saved and returned. - * - * @param name - name of the label, not including the leading # - * @param value - text value of the label; optional - */ + * Adds a new label to this note. The label attribute is saved and returned. + * + * @param name - name of the label, not including the leading # + * @param value - text value of the label; optional + */ addLabel(name: string, value: string = "", isInheritable: boolean = false): BAttribute { return this.addAttribute(LABEL, name, value, isInheritable); } /** - * Adds a new relation to this note. The relation attribute is saved and - * returned. - * - * @param name - name of the relation, not including the leading ~ - */ + * Adds a new relation to this note. The relation attribute is saved and + * returned. + * + * @param name - name of the relation, not including the leading ~ + */ addRelation(name: string, targetNoteId: string, isInheritable: boolean = false): BAttribute { return this.addAttribute(RELATION, name, targetNoteId, isInheritable); } /** - * Based on enabled, the attribute is either set or removed. - * - * @param type - attribute type ('relation', 'label' etc.) - * @param enabled - toggle On or Off - * @param name - attribute name - * @param value - attribute value (optional) - */ + * Based on enabled, the attribute is either set or removed. + * + * @param type - attribute type ('relation', 'label' etc.) + * @param enabled - toggle On or Off + * @param name - attribute name + * @param value - attribute value (optional) + */ toggleAttribute(type: AttributeType, enabled: boolean, name: string, value?: string) { if (enabled) { this.setAttribute(type, name, value); @@ -1361,63 +1361,63 @@ class BNote extends AbstractBeccaEntity { } /** - * Based on enabled, label is either set or removed. - * - * @param enabled - toggle On or Off - * @param name - label name - * @param value - label value (optional) - */ + * Based on enabled, label is either set or removed. + * + * @param enabled - toggle On or Off + * @param name - label name + * @param value - label value (optional) + */ toggleLabel(enabled: boolean, name: string, value?: string) { return this.toggleAttribute(LABEL, enabled, name, value); } /** - * Based on enabled, relation is either set or removed. - * - * @param enabled - toggle On or Off - * @param name - relation name - * @param value - relation value (noteId) - */ + * Based on enabled, relation is either set or removed. + * + * @param enabled - toggle On or Off + * @param name - relation name + * @param value - relation value (noteId) + */ toggleRelation(enabled: boolean, name: string, value?: string) { return this.toggleAttribute(RELATION, enabled, name, value); } /** - * Update's given label's value or creates it if it doesn't exist - * - * @param name - label name - * @param value label value - */ + * Update's given label's value or creates it if it doesn't exist + * + * @param name - label name + * @param value label value + */ setLabel(name: string, value?: string) { return this.setAttribute(LABEL, name, value); } /** - * Update's given relation's value or creates it if it doesn't exist - * - * @param name - relation name - * @param value - relation value (noteId) - */ + * Update's given relation's value or creates it if it doesn't exist + * + * @param name - relation name + * @param value - relation value (noteId) + */ setRelation(name: string, value?: string) { return this.setAttribute(RELATION, name, value); } /** - * Remove label name-value pair, if it exists. - * - * @param name - label name - * @param value - label value - */ + * Remove label name-value pair, if it exists. + * + * @param name - label name + * @param value - label value + */ removeLabel(name: string, value?: string) { return this.removeAttribute(LABEL, name, value); } /** - * Remove the relation name-value pair, if it exists. - * - * @param name - relation name - * @param value - relation value (noteId) - */ + * Remove the relation name-value pair, if it exists. + * + * @param name - relation name + * @param value - relation value (noteId) + */ removeRelation(name: string, value?: string) { return this.removeAttribute(RELATION, name, value); } @@ -1468,20 +1468,20 @@ class BNote extends AbstractBeccaEntity { } /** - * Some notes are eligible for conversion into an attachment of its parent, note must have these properties: - * - it has exactly one target relation - * - it has a relation from its parent note - * - it has no children - * - it has no clones - * - the parent is of type text - * - both notes are either unprotected or user is in protected session - * - * Currently, works only for image notes. - * - * In the future, this functionality might get more generic and some of the requirements relaxed. - * - * @returns null if note is not eligible for conversion - */ + * Some notes are eligible for conversion into an attachment of its parent, note must have these properties: + * - it has exactly one target relation + * - it has a relation from its parent note + * - it has no children + * - it has no clones + * - the parent is of type text + * - both notes are either unprotected or user is in protected session + * + * Currently, works only for image notes. + * + * In the future, this functionality might get more generic and some of the requirements relaxed. + * + * @returns null if note is not eligible for conversion + */ convertToParentAttachment(opts: ConvertOpts = { autoConversion: false }): BAttachment | null { if (!this.isEligibleForConversionToAttachment(opts)) { return null; @@ -1518,10 +1518,10 @@ class BNote extends AbstractBeccaEntity { } /** - * (Soft) delete a note and all its descendants. - * - * @param deleteId - optional delete identified - */ + * (Soft) delete a note and all its descendants. + * + * @param deleteId - optional delete identified + */ deleteNote(deleteId: string | null = null, taskContext: TaskContext | null = null) { if (this.isDeleted) { return; @@ -1640,9 +1640,9 @@ class BNote extends AbstractBeccaEntity { } /** - * @param matchBy - choose by which property we detect if to update an existing attachment. + * @param matchBy - choose by which property we detect if to update an existing attachment. * Supported values are either 'attachmentId' (default) or 'title' - */ + */ saveAttachment({attachmentId, role, mime, title, content, position}: AttachmentRow, matchBy = 'attachmentId') { if (!['attachmentId', 'title'].includes(matchBy)) { throw new Error(`Unsupported value '${matchBy}' for matchBy param, has to be either 'attachmentId' or 'title'.`); diff --git a/src/becca/entities/brevision.ts b/src/becca/entities/brevision.ts index 7448416ab..8ba6fbead 100644 --- a/src/becca/entities/brevision.ts +++ b/src/becca/entities/brevision.ts @@ -81,19 +81,19 @@ class BRevision extends AbstractBeccaEntity { } /* - * Note revision content has quite special handling - it's not a separate entity, but a lazily loaded - * part of Revision entity with its own sync. The reason behind this hybrid design is that - * content can be quite large, and it's not necessary to load it / fill memory for any note access even - * if we don't need a content, especially for bulk operations like search. - * - * This is the same approach as is used for Note's content. - */ + * Note revision content has quite special handling - it's not a separate entity, but a lazily loaded + * part of Revision entity with its own sync. The reason behind this hybrid design is that + * content can be quite large, and it's not necessary to load it / fill memory for any note access even + * if we don't need a content, especially for bulk operations like search. + * + * This is the same approach as is used for Note's content. + */ getContent(): string | Buffer { return this._getContent(); } /** - * @throws Error in case of invalid JSON */ + * @throws Error in case of invalid JSON */ getJsonContent(): {} | null { const content = this.getContent(); @@ -121,9 +121,9 @@ class BRevision extends AbstractBeccaEntity { getAttachments(): BAttachment[] { return sql.getRows(` SELECT attachments.* - FROM attachments - WHERE ownerId = ? - AND isDeleted = 0`, [this.revisionId]) + FROM attachments + WHERE ownerId = ? + AND isDeleted = 0`, [this.revisionId]) .map(row => new BAttachment(row)); } @@ -132,9 +132,9 @@ class BRevision extends AbstractBeccaEntity { const query = opts.includeContentLength ? `SELECT attachments.*, LENGTH(blobs.content) AS contentLength - FROM attachments - JOIN blobs USING (blobId) - WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0` + FROM attachments + JOIN blobs USING (blobId) + WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0` : `SELECT * FROM attachments WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0`; return sql.getRows(query, [this.revisionId, attachmentId]) @@ -144,10 +144,10 @@ class BRevision extends AbstractBeccaEntity { getAttachmentsByRole(role: string): BAttachment[] { return sql.getRows(` SELECT attachments.* - FROM attachments - WHERE ownerId = ? - AND role = ? - AND isDeleted = 0 + FROM attachments + WHERE ownerId = ? + AND role = ? + AND isDeleted = 0 ORDER BY position`, [this.revisionId, role]) .map(row => new BAttachment(row)); } @@ -158,8 +158,8 @@ class BRevision extends AbstractBeccaEntity { } /** - * Revisions are not soft-deletable, they are immediately hard-deleted (erased). - */ + * Revisions are not soft-deletable, they are immediately hard-deleted (erased). + */ eraseRevision() { if (this.revisionId) { eraseService.eraseRevisions([this.revisionId]); diff --git a/src/becca/entities/rows.ts b/src/becca/entities/rows.ts index 3f328f40d..bced96c78 100644 --- a/src/becca/entities/rows.ts +++ b/src/becca/entities/rows.ts @@ -40,7 +40,7 @@ export interface RecentNoteRow { /** * Database representation of an option. - * + * * Options are key-value pairs that are used to store information such as user preferences (for example * the current theme, sync server information), but also information about the state of the application). */ diff --git a/src/becca/similarity.ts b/src/becca/similarity.ts index 05b91f8ed..3c9b071b4 100644 --- a/src/becca/similarity.ts +++ b/src/becca/similarity.ts @@ -369,12 +369,12 @@ async function findSimilarNotes(noteId: string) { } /** - * We want to improve the standing of notes which have been created in similar time to each other since - * there's a good chance they are related. - * - * But there's an exception - if they were created really close to each other (within few seconds) then - * they are probably part of the import and not created by hand - these OTOH should not benefit. - */ + * We want to improve the standing of notes which have been created in similar time to each other since + * there's a good chance they are related. + * + * But there's an exception - if they were created really close to each other (within few seconds) then + * they are probably part of the import and not created by hand - these OTOH should not benefit. + */ const {utcDateCreated} = candidateNote; if (utcDateCreated < dateLimits.minExcludedDate || utcDateCreated > dateLimits.maxExcludedDate) { diff --git a/src/errors/not_found_error.ts b/src/errors/not_found_error.ts index f6ffb0d7e..6d8fbe4d8 100644 --- a/src/errors/not_found_error.ts +++ b/src/errors/not_found_error.ts @@ -6,4 +6,4 @@ class NotFoundError { } } -export default NotFoundError; \ No newline at end of file +export default NotFoundError; diff --git a/src/errors/validation_error.ts b/src/errors/validation_error.ts index 565082466..f9c0ba6fc 100644 --- a/src/errors/validation_error.ts +++ b/src/errors/validation_error.ts @@ -6,4 +6,4 @@ class ValidationError { } } -export default ValidationError; \ No newline at end of file +export default ValidationError; diff --git a/src/etapi/etapi-interface.ts b/src/etapi/etapi-interface.ts index c0bfef6d9..b4e816991 100644 --- a/src/etapi/etapi-interface.ts +++ b/src/etapi/etapi-interface.ts @@ -1,3 +1,3 @@ export type ValidatorFunc = (obj: unknown) => (string | undefined); -export type ValidatorMap = Record; \ No newline at end of file +export type ValidatorMap = Record; diff --git a/src/express.d.ts b/src/express.d.ts index 46f9521c4..e7b6393f1 100644 --- a/src/express.d.ts +++ b/src/express.d.ts @@ -8,7 +8,7 @@ export declare module "express-serve-static-core" { headers: { "x-local-date"?: string; "x-labels"?: string; - + "authorization"?: string; "trilium-cred"?: string; "x-csrf-token"?: string; @@ -18,4 +18,4 @@ export declare module "express-serve-static-core" { "trilium-hoisted-note-id"?: string; } } -} \ No newline at end of file +} diff --git a/src/main.ts b/src/main.ts index c38b85933..45ee3fd19 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,4 +10,4 @@ async function startApplication() { } await initializeTranslations(); -await startApplication(); \ No newline at end of file +await startApplication(); diff --git a/src/share/shaca/shaca-interface.ts b/src/share/shaca/shaca-interface.ts index f568e9cec..ad255540a 100644 --- a/src/share/shaca/shaca-interface.ts +++ b/src/share/shaca/shaca-interface.ts @@ -93,4 +93,4 @@ export default class Shaca { return (this as any)[camelCaseEntityName][entityId]; } -} \ No newline at end of file +} diff --git a/src/share/shaca/shaca_loader.ts b/src/share/shaca/shaca_loader.ts index aab4bca24..fd640ab91 100644 --- a/src/share/shaca/shaca_loader.ts +++ b/src/share/shaca/shaca_loader.ts @@ -23,7 +23,7 @@ function load() { SELECT ? UNION SELECT branches.noteId FROM branches - JOIN tree ON branches.parentNoteId = tree.noteId + JOIN tree ON branches.parentNoteId = tree.noteId WHERE branches.isDeleted = 0 ) SELECT noteId FROM tree`, [shareRoot.SHARE_ROOT_NOTE_ID]); @@ -38,19 +38,19 @@ function load() { const rawNoteRows = sql.getRawRows(` SELECT noteId, title, type, mime, blobId, utcDateModified, isProtected - FROM notes - WHERE isDeleted = 0 - AND noteId IN (${noteIdStr})`); + FROM notes + WHERE isDeleted = 0 + AND noteId IN (${noteIdStr})`); for (const row of rawNoteRows) { new SNote(row); } const rawBranchRows = sql.getRawRows(` - SELECT branchId, noteId, parentNoteId, prefix, isExpanded, utcDateModified - FROM branches - WHERE isDeleted = 0 - AND parentNoteId IN (${noteIdStr}) + SELECT branchId, noteId, parentNoteId, prefix, isExpanded, utcDateModified + FROM branches + WHERE isDeleted = 0 + AND parentNoteId IN (${noteIdStr}) ORDER BY notePosition`); for (const row of rawBranchRows) { @@ -58,20 +58,20 @@ function load() { } const rawAttributeRows = sql.getRawRows(` - SELECT attributeId, noteId, type, name, value, isInheritable, position, utcDateModified - FROM attributes - WHERE isDeleted = 0 - AND noteId IN (${noteIdStr})`); + SELECT attributeId, noteId, type, name, value, isInheritable, position, utcDateModified + FROM attributes + WHERE isDeleted = 0 + AND noteId IN (${noteIdStr})`); for (const row of rawAttributeRows) { new SAttribute(row); } const rawAttachmentRows = sql.getRawRows(` - SELECT attachmentId, ownerId, role, mime, title, blobId, utcDateModified - FROM attachments - WHERE isDeleted = 0 - AND ownerId IN (${noteIdStr})`); + SELECT attachmentId, ownerId, role, mime, title, blobId, utcDateModified + FROM attachments + WHERE isDeleted = 0 + AND ownerId IN (${noteIdStr})`); for (const row of rawAttachmentRows) { new SAttachment(row); diff --git a/src/share/sql.ts b/src/share/sql.ts index 87e9cf133..43664aa2a 100644 --- a/src/share/sql.ts +++ b/src/share/sql.ts @@ -8,7 +8,7 @@ let dbConnection!: Database.Database; sql_init.dbReady.then(() => { dbConnection = new Database(dataDir.DOCUMENT_PATH, { readonly: true }); - + [`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `SIGTERM`].forEach(eventType => { process.on(eventType, () => { if (dbConnection) { diff --git a/src/types.d.ts b/src/types.d.ts index a956b0e08..1ceedddb5 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -29,4 +29,4 @@ declare module 'joplin-turndown-plugin-gfm' { declare module 'is-animated' { function isAnimated(buffer: Buffer): boolean; export default isAnimated; -} \ No newline at end of file +} diff --git a/src/www.ts b/src/www.ts index e7632c506..982aa3638 100644 --- a/src/www.ts +++ b/src/www.ts @@ -42,21 +42,21 @@ startTrilium(); async function startTrilium() { /** - * The intended behavior is to detect when a second instance is running, in that case open the old instance - * instead of the new one. This is complicated by the fact that it is possible to run multiple instances of Trilium - * if port and data dir are configured separately. This complication is the source of the following weird usage. - * - * The line below makes sure that the "second-instance" (process in window.ts) is fired. Normally it returns a boolean - * indicating whether another instance is running or not, but we ignore that and kill the app only based on the port conflict. - * - * A bit weird is that "second-instance" is triggered also on the valid usecases (different port/data dir) and - * focuses the existing window. But the new process is start as well and will steal the focus too, it will win, because - * its startup is slower than focusing the existing process/window. So in the end, it works out without having - * to do a complex evaluation. - */ + * The intended behavior is to detect when a second instance is running, in that case open the old instance + * instead of the new one. This is complicated by the fact that it is possible to run multiple instances of Trilium + * if port and data dir are configured separately. This complication is the source of the following weird usage. + * + * The line below makes sure that the "second-instance" (process in window.ts) is fired. Normally it returns a boolean + * indicating whether another instance is running or not, but we ignore that and kill the app only based on the port conflict. + * + * A bit weird is that "second-instance" is triggered also on the valid usecases (different port/data dir) and + * focuses the existing window. But the new process is start as well and will steal the focus too, it will win, because + * its startup is slower than focusing the existing process/window. So in the end, it works out without having + * to do a complex evaluation. + */ if (utils.isElectron()) { (await import('electron')).app.requestSingleInstanceLock(); - } + } log.info(JSON.stringify(appInfo, null, 2)); @@ -116,8 +116,8 @@ function startHttpServer() { } /** - * Listen on provided port, on all network interfaces. - */ + * Listen on provided port, on all network interfaces. + */ httpServer.keepAliveTimeout = 120000 * 5; const listenOnTcp = port !== 0; @@ -149,7 +149,7 @@ function startHttpServer() { if (utils.isElectron()) { import("electron").then(({ app, dialog }) => { // Not all situations require showing an error dialog. When Trilium is already open, - // clicking the shortcut, the software icon, or the taskbar icon, or when creating a new window, + // clicking the shortcut, the software icon, or the taskbar icon, or when creating a new window, // should simply focus on the existing window or open a new one, without displaying an error message. if ("code" in error && error.code == 'EADDRINUSE') { if (process.argv.includes('--new-window') || !app.requestSingleInstanceLock()) { diff --git a/tests-examples/demo-todo-app.spec.ts b/tests-examples/demo-todo-app.spec.ts index 8641cb5f5..52fe8db70 100644 --- a/tests-examples/demo-todo-app.spec.ts +++ b/tests-examples/demo-todo-app.spec.ts @@ -21,7 +21,7 @@ test.describe('New Todo', () => { // Make sure the list only has one todo item. await expect(page.getByTestId('todo-title')).toHaveText([ - TODO_ITEMS[0] + TODO_ITEMS[0] ]); // Create 2nd todo. @@ -30,8 +30,8 @@ test.describe('New Todo', () => { // Make sure the list now has two todo items. await expect(page.getByTestId('todo-title')).toHaveText([ - TODO_ITEMS[0], - TODO_ITEMS[1] + TODO_ITEMS[0], + TODO_ITEMS[1] ]); await checkNumberOfTodosInLocalStorage(page, 2); @@ -56,7 +56,7 @@ test.describe('New Todo', () => { // create a todo count locator const todoCount = page.getByTestId('todo-count') - + // Check test using different methods. await expect(page.getByText('3 items left')).toBeVisible(); await expect(todoCount).toHaveText('3 items left'); @@ -127,8 +127,8 @@ test.describe('Item', () => { // Create two items. for (const item of TODO_ITEMS.slice(0, 2)) { - await newTodo.fill(item); - await newTodo.press('Enter'); + await newTodo.fill(item); + await newTodo.press('Enter'); } // Check first item. @@ -152,8 +152,8 @@ test.describe('Item', () => { // Create two items. for (const item of TODO_ITEMS.slice(0, 2)) { - await newTodo.fill(item); - await newTodo.press('Enter'); + await newTodo.fill(item); + await newTodo.press('Enter'); } const firstTodo = page.getByTestId('todo-item').nth(0); @@ -183,9 +183,9 @@ test.describe('Item', () => { // Explicitly assert the new text value. await expect(todoItems).toHaveText([ - TODO_ITEMS[0], - 'buy some sausages', - TODO_ITEMS[2] + TODO_ITEMS[0], + 'buy some sausages', + TODO_ITEMS[2] ]); await checkTodosInLocalStorage(page, 'buy some sausages'); }); @@ -202,7 +202,7 @@ test.describe('Editing', () => { await todoItem.dblclick(); await expect(todoItem.getByRole('checkbox')).not.toBeVisible(); await expect(todoItem.locator('label', { - hasText: TODO_ITEMS[1], + hasText: TODO_ITEMS[1], })).not.toBeVisible(); await checkNumberOfTodosInLocalStorage(page, 3); }); @@ -214,9 +214,9 @@ test.describe('Editing', () => { await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur'); await expect(todoItems).toHaveText([ - TODO_ITEMS[0], - 'buy some sausages', - TODO_ITEMS[2], + TODO_ITEMS[0], + 'buy some sausages', + TODO_ITEMS[2], ]); await checkTodosInLocalStorage(page, 'buy some sausages'); }); @@ -228,9 +228,9 @@ test.describe('Editing', () => { await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter'); await expect(todoItems).toHaveText([ - TODO_ITEMS[0], - 'buy some sausages', - TODO_ITEMS[2], + TODO_ITEMS[0], + 'buy some sausages', + TODO_ITEMS[2], ]); await checkTodosInLocalStorage(page, 'buy some sausages'); }); @@ -242,8 +242,8 @@ test.describe('Editing', () => { await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter'); await expect(todoItems).toHaveText([ - TODO_ITEMS[0], - TODO_ITEMS[2], + TODO_ITEMS[0], + TODO_ITEMS[2], ]); }); @@ -260,7 +260,7 @@ test.describe('Counter', () => { test('should display the current number of todo items', async ({ page }) => { // create a new todo locator const newTodo = page.getByPlaceholder('What needs to be done?'); - + // create a todo count locator const todoCount = page.getByTestId('todo-count') @@ -308,8 +308,8 @@ test.describe('Persistence', () => { const newTodo = page.getByPlaceholder('What needs to be done?'); for (const item of TODO_ITEMS.slice(0, 2)) { - await newTodo.fill(item); - await newTodo.press('Enter'); + await newTodo.fill(item); + await newTodo.press('Enter'); } const todoItems = page.getByTestId('todo-item'); @@ -350,22 +350,22 @@ test.describe('Routing', () => { }); test('should respect the back button', async ({ page }) => { - const todoItem = page.getByTestId('todo-item'); + const todoItem = page.getByTestId('todo-item'); await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check(); await checkNumberOfCompletedTodosInLocalStorage(page, 1); await test.step('Showing all items', async () => { - await page.getByRole('link', { name: 'All' }).click(); - await expect(todoItem).toHaveCount(3); + await page.getByRole('link', { name: 'All' }).click(); + await expect(todoItem).toHaveCount(3); }); await test.step('Showing active items', async () => { - await page.getByRole('link', { name: 'Active' }).click(); + await page.getByRole('link', { name: 'Active' }).click(); }); await test.step('Showing completed items', async () => { - await page.getByRole('link', { name: 'Completed' }).click(); + await page.getByRole('link', { name: 'Completed' }).click(); }); await expect(todoItem).toHaveCount(1); @@ -393,7 +393,7 @@ test.describe('Routing', () => { test('should highlight the currently applied filter', async ({ page }) => { await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected'); - + //create locators for active and completed links const activeLink = page.getByRole('link', { name: 'Active' }); const completedLink = page.getByRole('link', { name: 'Completed' });