Merge branch 'develop' into refactor_replace-csurf

This commit is contained in:
Elian Doran 2025-01-14 20:32:52 +02:00 committed by GitHub
commit c8c501d717
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
134 changed files with 663 additions and 1036 deletions

1
.gitattributes vendored
View File

@ -1 +1,2 @@
package-lock.json linguist-generated=true
**/package-lock.json linguist-generated=true **/package-lock.json linguist-generated=true

View File

@ -16,27 +16,6 @@ env:
TEST_TAG: ${{ github.repository_owner }}/notes:test TEST_TAG: ${{ github.repository_owner }}/notes:test
jobs: jobs:
build_docker:
name: Build Docker image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- run: npm ci
- name: Run the TypeScript build
run: npx tsc
- name: Create server-package.json
run: cat package.json | grep -v electron > server-package.json
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
test_dev: test_dev:
name: Test development name: Test development
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -54,9 +33,34 @@ jobs:
- name: Run the TypeScript build - name: Run the TypeScript build
run: npx tsc run: npx tsc
build_docker:
name: Build Docker image
runs-on: ubuntu-latest
needs:
- test_dev
steps:
- uses: actions/checkout@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- run: npm ci
- name: Run the TypeScript build
run: npx tsc
- name: Create server-package.json
run: cat package.json | grep -v electron > server-package.json
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
test_docker: test_docker:
name: Check Docker build name: Check Docker build
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs:
- build_docker
strategy: strategy:
matrix: matrix:
include: include:

View File

@ -48,7 +48,11 @@ jobs:
node-version: 20 node-version: 20
cache: "npm" cache: "npm"
- run: npm ci - name: Install npm dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run the TypeScript build - name: Run the TypeScript build
run: npx tsc run: npx tsc
@ -68,7 +72,7 @@ jobs:
- name: Validate container run output - name: Validate container run output
run: | run: |
CONTAINER_ID=$(docker run -d --log-driver=journald --rm --name trilium_local ${{ env.TEST_TAG }}) CONTAINER_ID=$(docker run -d --log-driver=journald --rm --network=host -e TRILIUM_PORT=8082 --volume ./integration-tests/db:/home/node/trilium-data --name trilium_local ${{ env.TEST_TAG }})
echo "Container ID: $CONTAINER_ID" echo "Container ID: $CONTAINER_ID"
- name: Wait for the healthchecks to pass - name: Wait for the healthchecks to pass
@ -78,6 +82,15 @@ jobs:
wait-time: 50 wait-time: 50
require-status: running require-status: running
require-healthy: true require-healthy: true
- name: Run Playwright tests
run: TRILIUM_DOCKER=1 npx playwright test
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: Playwright report (${{ matrix.dockerfile }})
path: playwright-report/
retention-days: 30
# Print the entire log of the container thus far, regardless if the healthcheck failed or succeeded # Print the entire log of the container thus far, regardless if the healthcheck failed or succeeded
- name: Print entire log - name: Print entire log

57
e2e/i18n.spec.ts Normal file
View File

@ -0,0 +1,57 @@
import { test, expect, Page } from "@playwright/test";
import App from "./support/app";
test.afterEach(async ({ page, context }) => {
const app = new App(page, context);
// Ensure English is set after each locale change to avoid any leaks to other tests.
await app.setOption("locale", "en");
});
test("Displays translation on desktop", async ({ page, context }) => {
const app = new App(page, context);
await app.goto();
await expect(page.locator("#left-pane .quick-search input"))
.toHaveAttribute("placeholder", "Quick search");
});
test("Displays translation on mobile", async ({ page, context }) => {
const app = new App(page, context);
await app.goto({ isMobile: true });
await expect(page.locator("#mobile-sidebar-wrapper .quick-search input"))
.toHaveAttribute("placeholder", "Quick search");
});
test("Displays translations in Settings", async ({ page, context }) => {
const app = new App(page, context);
await app.goto();
await app.closeAllTabs();
await app.goToSettings();
await app.noteTree.getByText("Appearance").click();
await expect(app.currentNoteSplit).toContainText("Localization");
await expect(app.currentNoteSplit).toContainText("Language");
});
test("User can change language from settings", async ({ page, context }) => {
const app = new App(page, context);
await app.goto();
await app.closeAllTabs();
await app.goToSettings();
await app.noteTree.getByText("Appearance").click();
// Check that the default value (English) is set.
await expect(app.currentNoteSplit).toContainText("Theme");
const languageCombobox = await app.currentNoteSplit.getByRole("combobox").first();
await expect(languageCombobox).toHaveValue("en");
// Select Chinese and ensure the translation is set.
await languageCombobox.selectOption("cn");
await expect(app.currentNoteSplit).toContainText("主题", { timeout: 15000 });
// Select English again.
await languageCombobox.selectOption("en");
await expect(app.currentNoteSplit).toContainText("Language", { timeout: 15000 });
});

View File

@ -3,8 +3,8 @@ import App from "../support/app";
const NOTE_TITLE = "Trilium Integration Test DB"; const NOTE_TITLE = "Trilium Integration Test DB";
test("Can drag tabs around", async ({ page }) => { test("Can drag tabs around", async ({ page, context }) => {
const app = new App(page); const app = new App(page, context);
await app.goto(); await app.goto();
// [1]: Trilium Integration Test DB note // [1]: Trilium Integration Test DB note
@ -29,8 +29,8 @@ test("Can drag tabs around", async ({ page }) => {
await expect(app.getTab(0)).toContainText(NOTE_TITLE); await expect(app.getTab(0)).toContainText(NOTE_TITLE);
}); });
test("Can drag tab to new window", async ({ page }) => { test("Can drag tab to new window", async ({ page, context }) => {
const app = new App(page); const app = new App(page, context);
await app.goto(); await app.goto();
await app.closeAllTabs(); await app.closeAllTabs();
@ -49,11 +49,11 @@ test("Can drag tab to new window", async ({ page }) => {
await page.mouse.move(x, y + tabPos.height + 100, { steps: 5 }); await page.mouse.move(x, y + tabPos.height + 100, { steps: 5 });
await page.mouse.up(); await page.mouse.up();
} else { } else {
fail("Unable to determine tab position"); test.fail(true, "Unable to determine tab position");
} }
// Wait for the popup to show // Wait for the popup to show
const popup = await popupPromise; const popup = await popupPromise;
const popupApp = new App(popup); const popupApp = new App(popup, context);
await expect(popupApp.getActiveTab()).toHaveText(NOTE_TITLE); await expect(popupApp.getActiveTab()).toHaveText(NOTE_TITLE);
}); });

View File

@ -1,8 +1,8 @@
import { test, expect, Page } from "@playwright/test"; import { test, expect, Page } from "@playwright/test";
import App from "../support/app"; import App from "../support/app";
test("Displays lint warnings for backend script", async ({ page }) => { test("Displays lint warnings for backend script", async ({ page, context }) => {
const app = new App(page); const app = new App(page, context);
await app.goto(); await app.goto();
await app.closeAllTabs(); await app.closeAllTabs();
await app.goToNoteInNewTab("Backend script with lint warnings"); await app.goToNoteInNewTab("Backend script with lint warnings");
@ -10,7 +10,7 @@ test("Displays lint warnings for backend script", async ({ page }) => {
const codeEditor = app.currentNoteSplit.locator(".CodeMirror"); const codeEditor = app.currentNoteSplit.locator(".CodeMirror");
// Expect two warning signs in the gutter. // Expect two warning signs in the gutter.
expect(codeEditor.locator(".CodeMirror-gutter-wrapper .CodeMirror-lint-marker-warning")).toHaveCount(2); await expect(codeEditor.locator(".CodeMirror-gutter-wrapper .CodeMirror-lint-marker-warning")).toHaveCount(2);
// Hover over hello // Hover over hello
await codeEditor.getByText("hello").first().hover(); await codeEditor.getByText("hello").first().hover();
@ -21,8 +21,8 @@ test("Displays lint warnings for backend script", async ({ page }) => {
await expectTooltip(page, "'world' is defined but never used."); await expectTooltip(page, "'world' is defined but never used.");
}); });
test("Displays lint errors for backend script", async ({ page }) => { test("Displays lint errors for backend script", async ({ page, context }) => {
const app = new App(page); const app = new App(page, context);
await app.goto(); await app.goto();
await app.closeAllTabs(); await app.closeAllTabs();
await app.goToNoteInNewTab("Backend script with lint errors"); await app.goToNoteInNewTab("Backend script with lint errors");

22
e2e/note_types/mindmap.ts Normal file
View File

@ -0,0 +1,22 @@
import { test, expect, Page } from "@playwright/test";
import App from "../support/app";
test("displays simple map", async ({ page, context }) => {
const app = new App(page, context);
await app.goto();
await app.goToNoteInNewTab("Sample mindmap");
await expect(app.currentNoteSplit).toContainText("Hello world");
await expect(app.currentNoteSplit).toContainText("1");
await expect(app.currentNoteSplit).toContainText("1a");
});
test("displays note settings", async ({ page, context }) => {
const app = new App(page, context);
await app.goto();
await app.goToNoteInNewTab("Sample mindmap");
await app.currentNoteSplit.getByText("Hello world").click({ force: true });
const nodeMenu = app.currentNoteSplit.locator(".node-menu");
await expect(nodeMenu).toBeVisible();
});

View File

@ -1,8 +1,8 @@
import { test, expect, Page } from "@playwright/test"; import { test, expect, Page } from "@playwright/test";
import App from "../support/app"; import App from "../support/app";
test("Table of contents is displayed", async ({ page }) => { test("Table of contents is displayed", async ({ page, context }) => {
const app = new App(page); const app = new App(page, context);
await app.goto(); await app.goto();
await app.closeAllTabs(); await app.closeAllTabs();
await app.goToNoteInNewTab("Table of contents"); await app.goToNoteInNewTab("Table of contents");
@ -36,8 +36,8 @@ test("Table of contents is displayed", async ({ page }) => {
await expect(rootList.locator("> ol").nth(1).locator("> ol > ol > ol")).toHaveCount(1); await expect(rootList.locator("> ol").nth(1).locator("> ol > ol > ol")).toHaveCount(1);
}); });
test("Highlights list is displayed", async ({ page }) => { test("Highlights list is displayed", async ({ page, context }) => {
const app = new App(page); const app = new App(page, context);
await app.goto(); await app.goto();
await app.closeAllTabs(); await app.closeAllTabs();
await app.goToNoteInNewTab("Highlights list"); await app.goToNoteInNewTab("Highlights list");

View File

@ -1,27 +1,46 @@
import { Locator, Page, expect } from "@playwright/test"; import { expect, Locator, Page } from "@playwright/test";
import type { BrowserContext } from "@playwright/test";
interface GotoOpts {
isMobile?: boolean;
}
const BASE_URL = "http://127.0.0.1:8082";
export default class App { export default class App {
readonly page: Page; readonly page: Page;
readonly context: BrowserContext;
readonly tabBar: Locator; readonly tabBar: Locator;
readonly noteTree: Locator; readonly noteTree: Locator;
readonly currentNoteSplit: Locator; readonly currentNoteSplit: Locator;
readonly sidebar: Locator; readonly sidebar: Locator;
constructor(page: Page) { constructor(page: Page, context: BrowserContext) {
this.page = page; this.page = page;
this.context = context;
this.tabBar = page.locator(".tab-row-widget-container"); this.tabBar = page.locator(".tab-row-widget-container");
this.noteTree = page.locator(".tree-wrapper"); this.noteTree = page.locator(".tree-wrapper");
this.currentNoteSplit = page.locator(".note-split:not(.hidden-ext)") this.currentNoteSplit = page.locator(".note-split:not(.hidden-ext)")
this.sidebar = page.locator("#right-pane"); this.sidebar = page.locator("#right-pane");
} }
async goto() { async goto(opts: GotoOpts = {}) {
await this.context.addCookies([
{
url: BASE_URL,
name: "trilium-device",
value: opts.isMobile ? "mobile" : "desktop"
}
]);
await this.page.goto("/", { waitUntil: "networkidle" }); await this.page.goto("/", { waitUntil: "networkidle" });
// Wait for the page to load. // Wait for the page to load.
await expect(this.page.locator(".tree")) await expect(this.page.locator(".tree"))
.toContainText("Trilium Integration Test"); .toContainText("Trilium Integration Test");
await this.closeAllTabs();
} }
async goToNoteInNewTab(noteTitle: string) { async goToNoteInNewTab(noteTitle: string) {
@ -31,6 +50,10 @@ export default class App {
await autocomplete.press("Enter"); await autocomplete.press("Enter");
} }
async goToSettings() {
await this.page.locator(".launcher-button.bx-cog").click();
}
getTab(tabIndex: number) { getTab(tabIndex: number) {
return this.tabBar.locator(".note-tab-wrapper").nth(tabIndex); return this.tabBar.locator(".note-tab-wrapper").nth(tabIndex);
} }
@ -39,18 +62,49 @@ export default class App {
return this.tabBar.locator(".note-tab[active]"); return this.tabBar.locator(".note-tab[active]");
} }
/**
* Closes all the tabs in the client by issuing a command.
*/
async closeAllTabs() { async closeAllTabs() {
await this.getTab(0).click({ button: "right" }); await this.triggerCommand("closeAllTabs");
await this.page.waitForTimeout(500); // TODO: context menu won't dismiss otherwise
await this.page.getByText("Close all tabs").click({ force: true });
} }
/**
* Adds a new tab by cliking on the + button near the tab bar.
*/
async addNewTab() { async addNewTab() {
await this.page.locator('[data-trigger-command="openNewTab"]').click(); await this.page.locator('[data-trigger-command="openNewTab"]').click();
} }
/**
* Looks for a given title in the note tree and clicks on it. Useful for selecting option pages in settings in a similar fashion as the user.
* @param title the title of the note to click, as displayed in the note tree.
*/
async clickNoteOnNoteTreeByTitle(title: string) { async clickNoteOnNoteTreeByTitle(title: string) {
this.noteTree.getByText(title).click(); await this.noteTree.getByText(title).click();
}
/**
* Executes any Trilium command on the client.
* @param command the command to send.
*/
async triggerCommand(command: string) {
await this.page.evaluate(async (command: string) => {
await (window as any).glob.appContext.triggerCommand(command);
}, command);
}
async setOption(key: string, value: string) {
const csrfToken = await this.page.evaluate(() => {
return (window as any).glob.csrfToken;
});
expect(csrfToken).toBeTruthy();
await expect(await this.page.request.put(`${BASE_URL}/api/options/${key}/${value}`, {
headers: {
"x-csrf-token": csrfToken
}
})).toBeOK();
} }
} }

Binary file not shown.

View File

@ -1,43 +0,0 @@
import test, { expect } from "@playwright/test";
test("User can change language from settings", async ({ page }) => {
await page.goto("http://localhost:8082");
// Clear all tabs
await page.locator(".note-tab:first-of-type").locator("div").nth(1).click({ button: "right" });
await page.getByText("Close all tabs").click();
// Go to options -> Appearance
await page.locator("#launcher-pane div").filter({ hasText: "Options Open New Window" }).getByRole("button").click();
await page.locator("#launcher-pane").getByText("Options").click();
await page.locator("#center-pane").getByText("Appearance").click();
// Check that the default value (English) is set.
await expect(page.locator("#center-pane")).toContainText("Theme");
const languageCombobox = await page.getByRole("combobox").first();
await expect(languageCombobox).toHaveValue("en");
// Select Chinese and ensure the translation is set.
languageCombobox.selectOption("cn");
await expect(page.locator("#center-pane")).toContainText("主题");
// Select English again.
languageCombobox.selectOption("en");
});
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");
});
test("Restores language on start-up on mobile", async ({ page, context }) => {
await context.addCookies([
{
url: "http://localhost:8082",
name: "trilium-device",
value: "mobile"
}
]);
await page.goto("http://localhost:8082");
await expect(page.locator("#launcher-pane div").first()).toContainText("Open New Window");
});

670
package-lock.json generated
View File

@ -70,7 +70,7 @@
"marked": "15.0.6", "marked": "15.0.6",
"mermaid": "11.4.1", "mermaid": "11.4.1",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"mind-elixir": "4.3.5", "mind-elixir": "4.3.6",
"multer": "1.4.5-lts.1", "multer": "1.4.5-lts.1",
"normalize-strings": "1.1.1", "normalize-strings": "1.1.1",
"normalize.css": "8.0.1", "normalize.css": "8.0.1",
@ -91,7 +91,7 @@
"stream-throttle": "0.1.3", "stream-throttle": "0.1.3",
"striptags": "3.2.0", "striptags": "3.2.0",
"tmp": "0.2.3", "tmp": "0.2.3",
"ts-loader": "9.5.1", "ts-loader": "9.5.2",
"turndown": "7.2.0", "turndown": "7.2.0",
"unescape": "1.0.1", "unescape": "1.0.1",
"vanilla-js-wheel-zoom": "9.0.4", "vanilla-js-wheel-zoom": "9.0.4",
@ -109,6 +109,7 @@
"@electron-forge/maker-squirrel": "7.6.0", "@electron-forge/maker-squirrel": "7.6.0",
"@electron-forge/maker-zip": "7.6.0", "@electron-forge/maker-zip": "7.6.0",
"@electron-forge/plugin-auto-unpack-natives": "7.6.0", "@electron-forge/plugin-auto-unpack-natives": "7.6.0",
"@electron/rebuild": "3.7.1",
"@playwright/test": "1.49.1", "@playwright/test": "1.49.1",
"@types/archiver": "6.0.3", "@types/archiver": "6.0.3",
"@types/better-sqlite3": "7.6.12", "@types/better-sqlite3": "7.6.12",
@ -131,7 +132,7 @@
"@types/jsdom": "21.1.7", "@types/jsdom": "21.1.7",
"@types/mime-types": "2.1.4", "@types/mime-types": "2.1.4",
"@types/multer": "1.4.12", "@types/multer": "1.4.12",
"@types/node": "22.10.5", "@types/node": "22.10.6",
"@types/safe-compare": "1.1.2", "@types/safe-compare": "1.1.2",
"@types/sanitize-html": "2.13.0", "@types/sanitize-html": "2.13.0",
"@types/sax": "1.2.7", "@types/sax": "1.2.7",
@ -147,8 +148,6 @@
"@types/yargs": "17.0.33", "@types/yargs": "17.0.33",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"electron": "33.3.1", "electron": "33.3.1",
"electron-packager": "17.1.2",
"electron-rebuild": "3.2.9",
"esm": "3.2.25", "esm": "3.2.25",
"iconsur": "1.7.0", "iconsur": "1.7.0",
"jasmine": "5.5.0", "jasmine": "5.5.0",
@ -4333,9 +4332,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.10.5", "version": "22.10.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.6.tgz",
"integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==", "integrity": "sha512-qNiuwC4ZDAUNcY47xgaSuS92cjf8JbSUoaKS77bmLG1rU7MlATVSiw/IlrjtIyyskXBZ8KkNfjK/P5na7rgXbQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -5098,13 +5097,6 @@
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/aproba": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
"dev": true,
"license": "ISC"
},
"node_modules/archiver": { "node_modules/archiver": {
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz",
@ -5194,36 +5186,6 @@
"node": ">=16 || 14 >=14.17" "node": ">=16 || 14 >=14.17"
} }
}, },
"node_modules/are-we-there-yet": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
"integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
"deprecated": "This package is no longer supported.",
"dev": true,
"license": "ISC",
"dependencies": {
"delegates": "^1.0.0",
"readable-stream": "^3.6.0"
},
"engines": {
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
"node_modules/are-we-there-yet/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/arg": { "node_modules/arg": {
"version": "4.1.3", "version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
@ -5807,19 +5769,6 @@
"node": ">=8.0.0" "node": ">=8.0.0"
} }
}, },
"node_modules/buffer-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz",
"integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/buffer-fill": { "node_modules/buffer-fill": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
@ -6520,16 +6469,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/color-support": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
"dev": true,
"license": "ISC",
"bin": {
"color-support": "bin.js"
}
},
"node_modules/colorette": { "node_modules/colorette": {
"version": "2.0.20", "version": "2.0.20",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
@ -6687,13 +6626,6 @@
"integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
"dev": true,
"license": "ISC"
},
"node_modules/content-disposition": { "node_modules/content-disposition": {
"version": "0.5.4", "version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@ -7706,13 +7638,6 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
"dev": true,
"license": "MIT"
},
"node_modules/depd": { "node_modules/depd": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@ -8344,307 +8269,6 @@
"keyboardevents-areequal": "^0.2.1" "keyboardevents-areequal": "^0.2.1"
} }
}, },
"node_modules/electron-packager": {
"version": "17.1.2",
"resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-17.1.2.tgz",
"integrity": "sha512-XofXdikjYI7MVBcnXeoOvRR+yFFFHOLs3J7PF5KYQweigtgLshcH4W660PsvHr4lYZ03JBpLyEcUB8DzHZ+BNw==",
"deprecated": "Please use @electron/packager moving forward. There is no API change, just a package name change",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@electron/asar": "^3.2.1",
"@electron/get": "^2.0.0",
"@electron/notarize": "^1.2.3",
"@electron/osx-sign": "^1.0.5",
"@electron/universal": "^1.3.2",
"cross-spawn-windows-exe": "^1.2.0",
"debug": "^4.0.1",
"extract-zip": "^2.0.0",
"filenamify": "^4.1.0",
"fs-extra": "^11.1.0",
"galactus": "^1.0.0",
"get-package-info": "^1.0.0",
"junk": "^3.1.0",
"parse-author": "^2.0.0",
"plist": "^3.0.0",
"rcedit": "^3.0.1",
"resolve": "^1.1.6",
"semver": "^7.1.3",
"yargs-parser": "^21.1.1"
},
"bin": {
"electron-packager": "bin/electron-packager.js"
},
"engines": {
"node": ">= 14.17.5"
},
"funding": {
"url": "https://github.com/electron/electron-packager?sponsor=1"
}
},
"node_modules/electron-packager/node_modules/@electron/get": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz",
"integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "^4.1.1",
"env-paths": "^2.2.0",
"fs-extra": "^8.1.0",
"got": "^11.8.5",
"progress": "^2.0.3",
"semver": "^6.2.0",
"sumchecker": "^3.0.1"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"global-agent": "^3.0.0"
}
},
"node_modules/electron-packager/node_modules/@electron/get/node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"engines": {
"node": ">=6 <7 || >=8"
}
},
"node_modules/electron-packager/node_modules/@electron/get/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/electron-packager/node_modules/@electron/get/node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/electron-packager/node_modules/@electron/notarize": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-1.2.4.tgz",
"integrity": "sha512-W5GQhJEosFNafewnS28d3bpQ37/s91CDWqxVchHfmv2dQSTWpOzNlUVQwYzC1ay5bChRV/A9BTL68yj0Pa+TSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "^4.1.1",
"fs-extra": "^9.0.1"
},
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/electron-packager/node_modules/@electron/notarize/node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/electron-packager/node_modules/@electron/notarize/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/electron-packager/node_modules/@electron/universal": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz",
"integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@electron/asar": "^3.2.1",
"@malept/cross-spawn-promise": "^1.1.0",
"debug": "^4.3.1",
"dir-compare": "^3.0.0",
"fs-extra": "^9.0.1",
"minimatch": "^3.0.4",
"plist": "^3.0.4"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/electron-packager/node_modules/@electron/universal/node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/electron-packager/node_modules/@electron/universal/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/electron-packager/node_modules/@malept/cross-spawn-promise": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz",
"integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/malept"
},
{
"type": "tidelift",
"url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund"
}
],
"license": "Apache-2.0",
"dependencies": {
"cross-spawn": "^7.0.1"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/electron-packager/node_modules/dir-compare": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz",
"integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-equal": "^1.0.0",
"minimatch": "^3.0.4"
}
},
"node_modules/electron-packager/node_modules/rcedit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/rcedit/-/rcedit-3.1.0.tgz",
"integrity": "sha512-WRlRdY1qZbu1L11DklT07KuHfRk42l0NFFJdaExELEu4fEQ982bP5Z6OWGPj/wLLIuKRQDCxZJGAwoFsxhZhNA==",
"dev": true,
"license": "MIT",
"dependencies": {
"cross-spawn-windows-exe": "^1.1.0"
},
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/electron-rebuild": {
"version": "3.2.9",
"resolved": "https://registry.npmjs.org/electron-rebuild/-/electron-rebuild-3.2.9.tgz",
"integrity": "sha512-FkEZNFViUem3P0RLYbZkUjC8LUFIK+wKq09GHoOITSJjfDAVQv964hwaNseTTWt58sITQX3/5fHNYcTefqaCWw==",
"deprecated": "Please use @electron/rebuild moving forward. There is no API change, just a package name change",
"dev": true,
"license": "MIT",
"dependencies": {
"@malept/cross-spawn-promise": "^2.0.0",
"chalk": "^4.0.0",
"debug": "^4.1.1",
"detect-libc": "^2.0.1",
"fs-extra": "^10.0.0",
"got": "^11.7.0",
"lzma-native": "^8.0.5",
"node-abi": "^3.0.0",
"node-api-version": "^0.1.4",
"node-gyp": "^9.0.0",
"ora": "^5.1.0",
"semver": "^7.3.5",
"tar": "^6.0.5",
"yargs": "^17.0.1"
},
"bin": {
"electron-rebuild": "lib/src/cli.js"
},
"engines": {
"node": ">=12.13.0"
}
},
"node_modules/electron-rebuild/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/electron-rebuild/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/electron-rebuild/node_modules/node-api-version": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.1.4.tgz",
"integrity": "sha512-KGXihXdUChwJAOHO53bv9/vXcLmdUsZ6jIptbvYvkpKfth+r7jw44JkVxQFA3kX5nQjzjmGu1uAu/xNNLNlI5g==",
"dev": true,
"license": "MIT",
"dependencies": {
"semver": "^7.3.5"
}
},
"node_modules/electron-squirrel-startup": { "node_modules/electron-squirrel-startup": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/electron-squirrel-startup/-/electron-squirrel-startup-1.0.1.tgz", "resolved": "https://registry.npmjs.org/electron-squirrel-startup/-/electron-squirrel-startup-1.0.1.tgz",
@ -10101,82 +9725,6 @@
"license": "MIT", "license": "MIT",
"optional": true "optional": true
}, },
"node_modules/gauge": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
"integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
"deprecated": "This package is no longer supported.",
"dev": true,
"license": "ISC",
"dependencies": {
"aproba": "^1.0.3 || ^2.0.0",
"color-support": "^1.1.3",
"console-control-strings": "^1.1.0",
"has-unicode": "^2.0.1",
"signal-exit": "^3.0.7",
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"wide-align": "^1.1.5"
},
"engines": {
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
"node_modules/gauge/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/gauge/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/gauge/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/gauge/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/gauge/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/generate-function": { "node_modules/generate-function": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
@ -10553,13 +10101,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
"dev": true,
"license": "ISC"
},
"node_modules/hasown": { "node_modules/hasown": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@ -12397,40 +11938,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/lzma-native": {
"version": "8.0.6",
"resolved": "https://registry.npmjs.org/lzma-native/-/lzma-native-8.0.6.tgz",
"integrity": "sha512-09xfg67mkL2Lz20PrrDeNYZxzeW7ADtpYFbwSQh9U8+76RIzx5QsJBMy8qikv3hbUPfpy6hqwxt6FcGK81g9AA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"node-addon-api": "^3.1.0",
"node-gyp-build": "^4.2.1",
"readable-stream": "^3.6.0"
},
"bin": {
"lzmajs": "bin/lzmajs"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/lzma-native/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/macos-alias": { "node_modules/macos-alias": {
"version": "0.2.12", "version": "0.2.12",
"resolved": "https://registry.npmjs.org/macos-alias/-/macos-alias-0.2.12.tgz", "resolved": "https://registry.npmjs.org/macos-alias/-/macos-alias-0.2.12.tgz",
@ -12817,9 +12324,9 @@
} }
}, },
"node_modules/mind-elixir": { "node_modules/mind-elixir": {
"version": "4.3.5", "version": "4.3.6",
"resolved": "https://registry.npmjs.org/mind-elixir/-/mind-elixir-4.3.5.tgz", "resolved": "https://registry.npmjs.org/mind-elixir/-/mind-elixir-4.3.6.tgz",
"integrity": "sha512-I1Mxc/jCwHEDMecDjQVpc+WShmzrEnIv6+MnWPauJ0LAiOXMBQB/wpKqlF4bTp+kCqzOHMYryAvIWm0jvloZ8Q==", "integrity": "sha512-6E9DT5vOYJ7DMDFXJlAnKU3Q6ekwBkR48Tjo6PchEcxJjPURJsiIASxtIeZCfvp8V39N4WyIa3Yt7Q/SFQkVfw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/minimalistic-assert": { "node_modules/minimalistic-assert": {
@ -13086,13 +12593,6 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/node-addon-api": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==",
"dev": true,
"license": "MIT"
},
"node_modules/node-api-version": { "node_modules/node-api-version": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.0.tgz", "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.0.tgz",
@ -13145,61 +12645,6 @@
"webidl-conversions": "^3.0.0" "webidl-conversions": "^3.0.0"
} }
}, },
"node_modules/node-gyp": {
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz",
"integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"env-paths": "^2.2.0",
"exponential-backoff": "^3.1.1",
"glob": "^7.1.4",
"graceful-fs": "^4.2.6",
"make-fetch-happen": "^10.0.3",
"nopt": "^6.0.0",
"npmlog": "^6.0.0",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"tar": "^6.1.2",
"which": "^2.0.2"
},
"bin": {
"node-gyp": "bin/node-gyp.js"
},
"engines": {
"node": "^12.13 || ^14.13 || >=16"
}
},
"node_modules/node-gyp-build": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
"dev": true,
"license": "MIT",
"bin": {
"node-gyp-build": "bin.js",
"node-gyp-build-optional": "optional.js",
"node-gyp-build-test": "build-test.js"
}
},
"node_modules/node-gyp/node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/node-releases": { "node_modules/node-releases": {
"version": "2.0.19", "version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
@ -13406,23 +12851,6 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/npmlog": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
"integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
"deprecated": "This package is no longer supported.",
"dev": true,
"license": "ISC",
"dependencies": {
"are-we-there-yet": "^3.0.0",
"console-control-strings": "^1.1.0",
"gauge": "^4.0.3",
"set-blocking": "^2.0.0"
},
"engines": {
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
"node_modules/nth-check": { "node_modules/nth-check": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@ -15761,13 +15189,6 @@
"node": ">= 4.0.0" "node": ">= 4.0.0"
} }
}, },
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true,
"license": "ISC"
},
"node_modules/setprototypeof": { "node_modules/setprototypeof": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@ -16995,9 +16416,9 @@
} }
}, },
"node_modules/ts-loader": { "node_modules/ts-loader": {
"version": "9.5.1", "version": "9.5.2",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz",
"integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"chalk": "^4.1.0", "chalk": "^4.1.0",
@ -17941,71 +17362,6 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^1.0.2 || 2 || 3 || 4"
}
},
"node_modules/wide-align/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/wide-align/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/wide-align/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/wide-align/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wide-align/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wildcard": { "node_modules/wildcard": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz",

View File

@ -36,6 +36,7 @@
"build-frontend-docs": "rimraf ./docs/frontend_api && jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/basic_widget.js src/public/app/widgets/note_context_aware_widget.js src/public/app/widgets/right_panel_widget.js", "build-frontend-docs": "rimraf ./docs/frontend_api && jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/basic_widget.js src/public/app/widgets/note_context_aware_widget.js src/public/app/widgets/right_panel_widget.js",
"build-docs": "npm run build-backend-docs && npm run build-frontend-docs", "build-docs": "npm run build-backend-docs && npm run build-frontend-docs",
"webpack": "cross-env node --import ./loader-register.js node_modules/webpack/bin/webpack.js -c webpack.config.ts", "webpack": "cross-env node --import ./loader-register.js node_modules/webpack/bin/webpack.js -c webpack.config.ts",
"test-playwright": "playwright test",
"test-jasmine": "cross-env TRILIUM_DATA_DIR=./data-test tsx ./node_modules/jasmine/bin/jasmine.js", "test-jasmine": "cross-env TRILIUM_DATA_DIR=./data-test tsx ./node_modules/jasmine/bin/jasmine.js",
"test-es6": "tsx -r esm spec-es6/attribute_parser.spec.ts", "test-es6": "tsx -r esm spec-es6/attribute_parser.spec.ts",
"test": "npm run test-jasmine && npm run test-es6", "test": "npm run test-jasmine && npm run test-es6",
@ -115,7 +116,7 @@
"marked": "15.0.6", "marked": "15.0.6",
"mermaid": "11.4.1", "mermaid": "11.4.1",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"mind-elixir": "4.3.5", "mind-elixir": "4.3.6",
"multer": "1.4.5-lts.1", "multer": "1.4.5-lts.1",
"normalize-strings": "1.1.1", "normalize-strings": "1.1.1",
"normalize.css": "8.0.1", "normalize.css": "8.0.1",
@ -136,7 +137,7 @@
"stream-throttle": "0.1.3", "stream-throttle": "0.1.3",
"striptags": "3.2.0", "striptags": "3.2.0",
"tmp": "0.2.3", "tmp": "0.2.3",
"ts-loader": "9.5.1", "ts-loader": "9.5.2",
"turndown": "7.2.0", "turndown": "7.2.0",
"unescape": "1.0.1", "unescape": "1.0.1",
"vanilla-js-wheel-zoom": "9.0.4", "vanilla-js-wheel-zoom": "9.0.4",
@ -151,6 +152,7 @@
"@electron-forge/maker-squirrel": "7.6.0", "@electron-forge/maker-squirrel": "7.6.0",
"@electron-forge/maker-zip": "7.6.0", "@electron-forge/maker-zip": "7.6.0",
"@electron-forge/plugin-auto-unpack-natives": "7.6.0", "@electron-forge/plugin-auto-unpack-natives": "7.6.0",
"@electron/rebuild": "3.7.1",
"@playwright/test": "1.49.1", "@playwright/test": "1.49.1",
"@types/archiver": "6.0.3", "@types/archiver": "6.0.3",
"@types/better-sqlite3": "7.6.12", "@types/better-sqlite3": "7.6.12",
@ -173,7 +175,7 @@
"@types/jsdom": "21.1.7", "@types/jsdom": "21.1.7",
"@types/mime-types": "2.1.4", "@types/mime-types": "2.1.4",
"@types/multer": "1.4.12", "@types/multer": "1.4.12",
"@types/node": "22.10.5", "@types/node": "22.10.6",
"@types/safe-compare": "1.1.2", "@types/safe-compare": "1.1.2",
"@types/sanitize-html": "2.13.0", "@types/sanitize-html": "2.13.0",
"@types/sax": "1.2.7", "@types/sax": "1.2.7",
@ -189,8 +191,6 @@
"@types/yargs": "17.0.33", "@types/yargs": "17.0.33",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"electron": "33.3.1", "electron": "33.3.1",
"electron-packager": "17.1.2",
"electron-rebuild": "3.2.9",
"esm": "3.2.25", "esm": "3.2.25",
"iconsur": "1.7.0", "iconsur": "1.7.0",
"jasmine": "5.5.0", "jasmine": "5.5.0",

View File

@ -73,9 +73,9 @@ export default defineConfig({
], ],
/* Run your local dev server before starting the tests */ /* Run your local dev server before starting the tests */
webServer: { webServer: !process.env.TRILIUM_DOCKER ? {
command: 'npm run integration-mem-db-dev', command: 'npm run integration-mem-db-dev',
url: SERVER_URL, url: SERVER_URL,
// reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
}, } : undefined,
}); });

138
spec-es6/data_dir.spec.ts Normal file
View File

@ -0,0 +1,138 @@
import { describe, it, execute, expect } from "./mini_test.ts";
import { getPlatformAppDataDir, getDataDirs} from "../src/services/data_dir.ts"
describe("data_dir.ts unit tests", () => {
describe("#getPlatformAppDataDir()", () => {
type TestCaseGetPlatformAppDataDir = [
description: string,
fnValue: Parameters<typeof getPlatformAppDataDir>,
expectedValueFn: (val: ReturnType<typeof getPlatformAppDataDir>) => boolean
]
const testCases: TestCaseGetPlatformAppDataDir[] = [
[
"w/ unsupported OS it should return 'null'",
["aix", undefined],
(val) => val === null
],
[
"w/ win32 and no APPDATA set it should return 'null'",
["win32", undefined],
(val) => val === null
],
[
"w/ win32 and set APPDATA it should return set 'APPDATA'",
["win32", "AppData"],
(val) => val === "AppData"
],
[
"w/ linux it should return '/.local/share'",
["linux", undefined],
(val) => val !== null && val.endsWith("/.local/share")
],
[
"w/ linux and wrongly set APPDATA it should ignore APPDATA and return /.local/share",
["linux", "FakeAppData"],
(val) => val !== null && val.endsWith("/.local/share")
],
[
"w/ darwin it should return /Library/Application Support",
["darwin", undefined],
(val) => val !== null && val.endsWith("/Library/Application Support")
],
];
testCases.forEach(testCase => {
const [testDescription, value, isExpected] = testCase;
return it(testDescription, () => {
const actual = getPlatformAppDataDir(...value);
const result = isExpected(actual);
expect(result).toBeTruthy()
})
})
})
describe("#getTriliumDataDir", () => {
// TODO
})
describe("#getDataDirs()", () => {
const envKeys: Omit<keyof ReturnType<typeof getDataDirs>, "TRILIUM_DATA_DIR">[] = [
"DOCUMENT_PATH",
"BACKUP_DIR",
"LOG_DIR",
"ANONYMIZED_DB_DIR",
"CONFIG_INI_PATH",
];
const setMockedEnv = (prefix: string | null) => {
envKeys.forEach(key => {
if (prefix) {
process.env[`TRILIUM_${key}`] = `${prefix}_${key}`
} else {
delete process.env[`TRILIUM_${key}`]
}
})
};
it("w/ process.env values present, it should return an object using values from process.env", () => {
// set mocked values
const mockValuePrefix = "MOCK";
setMockedEnv(mockValuePrefix);
// get result
const result = getDataDirs(`${mockValuePrefix}_TRILIUM_DATA_DIR`);
for (const key in result) {
expect(result[key]).toEqual(`${mockValuePrefix}_${key}`)
}
})
it("w/ NO process.env values present, it should return an object using supplied TRILIUM_DATA_DIR as base", () => {
// make sure values are undefined
setMockedEnv(null);
const mockDataDir = "/home/test/MOCK_TRILIUM_DATA_DIR"
const result = getDataDirs(mockDataDir);
for (const key in result) {
expect(result[key].startsWith(mockDataDir)).toBeTruthy()
}
})
it("should ignore attempts to change a property on the returned object", () => {
// make sure values are undefined
setMockedEnv(null);
const mockDataDir = "/home/test/MOCK_TRILIUM_DATA_DIR"
const result = getDataDirs(mockDataDir);
//@ts-expect-error - attempt to change value of readonly property
result.BACKUP_DIR = "attempt to change";
for (const key in result) {
expect(result[key].startsWith(mockDataDir)).toBeTruthy()
}
})
})
});
execute()

View File

@ -3,7 +3,7 @@ import BBranch from "../../src/becca/entities/bbranch.js";
import BAttribute from "../../src/becca/entities/battribute.js"; import BAttribute from "../../src/becca/entities/battribute.js";
import becca from "../../src/becca/becca.js"; import becca from "../../src/becca/becca.js";
import randtoken from "rand-token"; import randtoken from "rand-token";
import SearchResult from "../../src/services/search/search_result.js"; import type SearchResult from "../../src/services/search/search_result.js";
import type { NoteType } from "../../src/becca/entities/rows.js"; import type { NoteType } from "../../src/becca/entities/rows.js";
randtoken.generator({ source: "crypto" }); randtoken.generator({ source: "crypto" });

View File

@ -1,6 +1,6 @@
import AndExp from "../../src/services/search/expressions/and.js"; import AndExp from "../../src/services/search/expressions/and.js";
import AttributeExistsExp from "../../src/services/search/expressions/attribute_exists.js"; import AttributeExistsExp from "../../src/services/search/expressions/attribute_exists.js";
import Expression from "../../src/services/search/expressions/expression.js"; import type Expression from "../../src/services/search/expressions/expression.js";
import LabelComparisonExp from "../../src/services/search/expressions/label_comparison.js"; import LabelComparisonExp from "../../src/services/search/expressions/label_comparison.js";
import NotExp from "../../src/services/search/expressions/not.js"; import NotExp from "../../src/services/search/expressions/not.js";
import NoteContentFulltextExp from "../../src/services/search/expressions/note_content_fulltext.js"; import NoteContentFulltextExp from "../../src/services/search/expressions/note_content_fulltext.js";

View File

@ -1,4 +1,4 @@
import child_process from "child_process"; import type child_process from "child_process";
let etapiAuthToken: string | undefined; let etapiAuthToken: string | undefined;

View File

@ -1,17 +1,17 @@
import sql from "../services/sql.js"; import sql from "../services/sql.js";
import NoteSet from "../services/search/note_set.js"; import NoteSet from "../services/search/note_set.js";
import NotFoundError from "../errors/not_found_error.js"; import NotFoundError from "../errors/not_found_error.js";
import BOption from "./entities/boption.js"; import type BOption from "./entities/boption.js";
import BNote from "./entities/bnote.js"; import type BNote from "./entities/bnote.js";
import BEtapiToken from "./entities/betapi_token.js"; import type BEtapiToken from "./entities/betapi_token.js";
import BAttribute from "./entities/battribute.js"; import type BAttribute from "./entities/battribute.js";
import BBranch from "./entities/bbranch.js"; import type BBranch from "./entities/bbranch.js";
import BRevision from "./entities/brevision.js"; import BRevision from "./entities/brevision.js";
import BAttachment from "./entities/battachment.js"; import BAttachment from "./entities/battachment.js";
import type { AttachmentRow, BlobRow, RevisionRow } from "./entities/rows.js"; import type { AttachmentRow, BlobRow, RevisionRow } from "./entities/rows.js";
import BBlob from "./entities/bblob.js"; import BBlob from "./entities/bblob.js";
import BRecentNote from "./entities/brecent_note.js"; import BRecentNote from "./entities/brecent_note.js";
import AbstractBeccaEntity from "./entities/abstract_becca_entity.js"; import type AbstractBeccaEntity from "./entities/abstract_becca_entity.js";
interface AttachmentOpts { interface AttachmentOpts {
includeContentLength?: boolean; includeContentLength?: boolean;

View File

@ -12,7 +12,7 @@ import BEtapiToken from "./entities/betapi_token.js";
import cls from "../services/cls.js"; import cls from "../services/cls.js";
import entityConstructor from "../becca/entity_constructor.js"; import entityConstructor from "../becca/entity_constructor.js";
import type { AttributeRow, BranchRow, EtapiTokenRow, NoteRow, OptionRow } from "./entities/rows.js"; import type { AttributeRow, BranchRow, EtapiTokenRow, NoteRow, OptionRow } from "./entities/rows.js";
import AbstractBeccaEntity from "./entities/abstract_becca_entity.js"; import type AbstractBeccaEntity from "./entities/abstract_becca_entity.js";
import ws from "../services/ws.js"; import ws from "../services/ws.js";
const beccaLoaded = new Promise<void>(async (res, rej) => { const beccaLoaded = new Promise<void>(async (res, rej) => {

View File

@ -9,7 +9,7 @@ import cls from "../../services/cls.js";
import log from "../../services/log.js"; import log from "../../services/log.js";
import protectedSessionService from "../../services/protected_session.js"; import protectedSessionService from "../../services/protected_session.js";
import blobService from "../../services/blob.js"; import blobService from "../../services/blob.js";
import Becca, { type ConstructorData } from "../becca-interface.js"; import type { default as Becca, ConstructorData } from "../becca-interface.js";
import becca from "../becca.js"; import becca from "../becca.js";
interface ContentOpts { interface ContentOpts {

View File

@ -7,8 +7,8 @@ import sql from "../../services/sql.js";
import protectedSessionService from "../../services/protected_session.js"; import protectedSessionService from "../../services/protected_session.js";
import log from "../../services/log.js"; import log from "../../services/log.js";
import type { AttachmentRow } from "./rows.js"; import type { AttachmentRow } from "./rows.js";
import BNote from "./bnote.js"; import type BNote from "./bnote.js";
import BBranch from "./bbranch.js"; import type BBranch from "./bbranch.js";
import noteService from "../../services/notes.js"; import noteService from "../../services/notes.js";
const attachmentRoleToNoteTypeMapping = { const attachmentRoleToNoteTypeMapping = {

View File

@ -15,7 +15,7 @@ import dayjs from "dayjs";
import utc from "dayjs/plugin/utc.js"; import utc from "dayjs/plugin/utc.js";
import eventService from "../../services/events.js"; import eventService from "../../services/events.js";
import type { AttachmentRow, AttributeType, NoteRow, NoteType, RevisionRow } from "./rows.js"; import type { AttachmentRow, AttributeType, NoteRow, NoteType, RevisionRow } from "./rows.js";
import BBranch from "./bbranch.js"; import type BBranch from "./bbranch.js";
import BAttribute from "./battribute.js"; import BAttribute from "./battribute.js";
import type { NotePojo } from "../becca-interface.js"; import type { NotePojo } from "../becca-interface.js";
import searchService from "../../services/search/services/search.js"; import searchService from "../../services/search/services/search.js";

View File

@ -1,5 +1,5 @@
import type { ConstructorData } from "./becca-interface.js"; import type { ConstructorData } from "./becca-interface.js";
import AbstractBeccaEntity from "./entities/abstract_becca_entity.js"; import type AbstractBeccaEntity from "./entities/abstract_becca_entity.js";
import BAttachment from "./entities/battachment.js"; import BAttachment from "./entities/battachment.js";
import BAttribute from "./entities/battribute.js"; import BAttribute from "./entities/battribute.js";
import BBlob from "./entities/bblob.js"; import BBlob from "./entities/bblob.js";

View File

@ -3,7 +3,7 @@ import log from "../services/log.js";
import beccaService from "./becca_service.js"; import beccaService from "./becca_service.js";
import dateUtils from "../services/date_utils.js"; import dateUtils from "../services/date_utils.js";
import { JSDOM } from "jsdom"; import { JSDOM } from "jsdom";
import BNote from "./entities/bnote.js"; import type BNote from "./entities/bnote.js";
const DEBUG = false; const DEBUG = false;

View File

@ -1,4 +1,4 @@
import { Router } from "express"; import type { Router } from "express";
import appInfo from "../services/app_info.js"; import appInfo from "../services/app_info.js";
import eu from "./etapi_utils.js"; import eu from "./etapi_utils.js";

View File

@ -3,7 +3,7 @@ import eu from "./etapi_utils.js";
import mappers from "./mappers.js"; import mappers from "./mappers.js";
import v from "./validators.js"; import v from "./validators.js";
import utils from "../services/utils.js"; import utils from "../services/utils.js";
import { Router } from "express"; import type { Router } from "express";
import type { AttachmentRow } from "../becca/entities/rows.js"; import type { AttachmentRow } from "../becca/entities/rows.js";
import type { ValidatorMap } from "./etapi-interface.js"; import type { ValidatorMap } from "./etapi-interface.js";

View File

@ -3,7 +3,7 @@ import eu from "./etapi_utils.js";
import mappers from "./mappers.js"; import mappers from "./mappers.js";
import attributeService from "../services/attributes.js"; import attributeService from "../services/attributes.js";
import v from "./validators.js"; import v from "./validators.js";
import { Router } from "express"; import type { Router } from "express";
import type { AttributeRow } from "../becca/entities/rows.js"; import type { AttributeRow } from "../becca/entities/rows.js";
import type { ValidatorMap } from "./etapi-interface.js"; import type { ValidatorMap } from "./etapi-interface.js";

View File

@ -1,4 +1,4 @@
import { Router } from "express"; import type { Router } from "express";
import eu from "./etapi_utils.js"; import eu from "./etapi_utils.js";
import backupService from "../services/backup.js"; import backupService from "../services/backup.js";

View File

@ -1,4 +1,4 @@
import { Router } from "express"; import type { Router } from "express";
import becca from "../becca/becca.js"; import becca from "../becca/becca.js";
import eu from "./etapi_utils.js"; import eu from "./etapi_utils.js";

View File

@ -1,7 +1,7 @@
import BAttachment from "../becca/entities/battachment.js"; import type BAttachment from "../becca/entities/battachment.js";
import BAttribute from "../becca/entities/battribute.js"; import type BAttribute from "../becca/entities/battribute.js";
import BBranch from "../becca/entities/bbranch.js"; import type BBranch from "../becca/entities/bbranch.js";
import BNote from "../becca/entities/bnote.js"; import type BNote from "../becca/entities/bnote.js";
function mapNoteToPojo(note: BNote) { function mapNoteToPojo(note: BNote) {
return { return {

View File

@ -9,7 +9,7 @@ import searchService from "../services/search/services/search.js";
import SearchContext from "../services/search/search_context.js"; import SearchContext from "../services/search/search_context.js";
import zipExportService from "../services/export/zip.js"; import zipExportService from "../services/export/zip.js";
import zipImportService from "../services/import/zip.js"; import zipImportService from "../services/import/zip.js";
import { type Request, Router } from "express"; import type { Request, Router } from "express";
import type { ParsedQs } from "qs"; import type { ParsedQs } from "qs";
import type { NoteParams } from "../services/note-interface.js"; import type { NoteParams } from "../services/note-interface.js";
import type { SearchParams } from "../services/search/services/types.js"; import type { SearchParams } from "../services/search/services/types.js";

View File

@ -1,4 +1,4 @@
import { Router } from "express"; import type { Router } from "express";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";

View File

@ -2,7 +2,7 @@ import specialNotesService from "../services/special_notes.js";
import dateNotesService from "../services/date_notes.js"; import dateNotesService from "../services/date_notes.js";
import eu from "./etapi_utils.js"; import eu from "./etapi_utils.js";
import mappers from "./mappers.js"; import mappers from "./mappers.js";
import { Router } from "express"; import type { Router } from "express";
const getDateInvalidError = (date: string) => new eu.EtapiError(400, "DATE_INVALID", `Date "${date}" is not valid.`); const getDateInvalidError = (date: string) => new eu.EtapiError(400, "DATE_INVALID", `Date "${date}" is not valid.`);
const getMonthInvalidError = (month: string) => new eu.EtapiError(400, "MONTH_INVALID", `Month "${month}" is not valid.`); const getMonthInvalidError = (month: string) => new eu.EtapiError(400, "MONTH_INVALID", `Month "${month}" is not valid.`);

View File

@ -14,15 +14,15 @@ import MainTreeExecutors from "./main_tree_executors.js";
import toast from "../services/toast.js"; import toast from "../services/toast.js";
import ShortcutComponent from "./shortcut_component.js"; import ShortcutComponent from "./shortcut_component.js";
import { t, initLocale } from "../services/i18n.js"; import { t, initLocale } from "../services/i18n.js";
import NoteDetailWidget from "../widgets/note_detail.js"; import type NoteDetailWidget from "../widgets/note_detail.js";
import type { ResolveOptions } from "../widgets/dialogs/delete_notes.js"; import type { ResolveOptions } from "../widgets/dialogs/delete_notes.js";
import type { PromptDialogOptions } from "../widgets/dialogs/prompt.js"; import type { PromptDialogOptions } from "../widgets/dialogs/prompt.js";
import type { ConfirmWithMessageOptions, ConfirmWithTitleOptions } from "../widgets/dialogs/confirm.js"; import type { ConfirmWithMessageOptions, ConfirmWithTitleOptions } from "../widgets/dialogs/confirm.js";
import type { Node } from "../services/tree.js"; import type { Node } from "../services/tree.js";
import LoadResults from "../services/load_results.js"; import type LoadResults from "../services/load_results.js";
import type { Attribute } from "../services/attribute_parser.js"; import type { Attribute } from "../services/attribute_parser.js";
import NoteTreeWidget from "../widgets/note_tree.js"; import type NoteTreeWidget from "../widgets/note_tree.js";
import NoteContext, { type GetTextEditorCallback } from "./note_context.js"; import type { default as NoteContext, GetTextEditorCallback } from "./note_context.js";
interface Layout { interface Layout {
getRootWidget: (appContext: AppContext) => RootWidget; getRootWidget: (appContext: AppContext) => RootWidget;
@ -92,7 +92,7 @@ export type CommandMappings = {
filePath: string; filePath: string;
}; };
focusAndSelectTitle: CommandData & { focusAndSelectTitle: CommandData & {
isNewNote: boolean; isNewNote?: boolean;
}; };
showPromptDialog: PromptDialogOptions; showPromptDialog: PromptDialogOptions;
showInfoDialog: ConfirmWithMessageOptions; showInfoDialog: ConfirmWithMessageOptions;
@ -262,6 +262,9 @@ type EventMappings = {
}; };
noteContextRemovedEvent: { noteContextRemovedEvent: {
ntxIds: string[]; ntxIds: string[];
};
exportSvg: {
ntxId: string;
} }
}; };
@ -274,15 +277,16 @@ export type CommandListener<T extends CommandNames> = {
}; };
export type CommandListenerData<T extends CommandNames> = CommandMappings[T]; export type CommandListenerData<T extends CommandNames> = CommandMappings[T];
export type EventData<T extends EventNames> = EventMappings[T];
type CommandAndEventMappings = CommandMappings & EventMappings; type CommandAndEventMappings = CommandMappings & EventMappings;
type EventOnlyNames = keyof EventMappings;
export type EventNames = CommandNames | EventOnlyNames;
export type EventData<T extends EventNames> = CommandAndEventMappings[T];
/** /**
* This type is a discriminated union which contains all the possible commands that can be triggered via {@link AppContext.triggerCommand}. * This type is a discriminated union which contains all the possible commands that can be triggered via {@link AppContext.triggerCommand}.
*/ */
export type CommandNames = keyof CommandMappings; export type CommandNames = keyof CommandMappings;
type EventNames = keyof EventMappings;
type FilterByValueType<T, ValueType> = { [K in keyof T]: T[K] extends ValueType ? K : never }[keyof T]; type FilterByValueType<T, ValueType> = { [K in keyof T]: T[K] extends ValueType ? K : never }[keyof T];
@ -375,12 +379,10 @@ class AppContext extends Component {
this.child(rootWidget); this.child(rootWidget);
this.triggerEvent("initialRenderComplete"); this.triggerEvent("initialRenderComplete", {});
} }
// TODO: Remove ignore once all commands are mapped out. triggerEvent<K extends EventNames>(name: K, data: EventData<K>) {
//@ts-ignore
triggerEvent<K extends EventNames | CommandNames>(name: K, data: CommandAndEventMappings[K] = {}) {
return this.handleEvent(name, data); return this.handleEvent(name, data);
} }

View File

@ -1,5 +1,5 @@
import utils from "../services/utils.js"; import utils from "../services/utils.js";
import type { CommandMappings, CommandNames } from "./app_context.js"; import type { CommandMappings, CommandNames, EventData, EventNames } from "./app_context.js";
/** /**
* Abstract class for all components in the Trilium's frontend. * Abstract class for all components in the Trilium's frontend.
@ -46,7 +46,7 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> {
return this; return this;
} }
handleEvent(name: string, data: unknown): Promise<unknown> | null { handleEvent<T extends EventNames>(name: T, data: EventData<T>): Promise<unknown[] | unknown> | null {
try { try {
const callMethodPromise = this.initialized ? this.initialized.then(() => this.callMethod((this as any)[`${name}Event`], data)) : this.callMethod((this as any)[`${name}Event`], data); const callMethodPromise = this.initialized ? this.initialized.then(() => this.callMethod((this as any)[`${name}Event`], data)) : this.callMethod((this as any)[`${name}Event`], data);
@ -65,11 +65,11 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> {
return this.parent?.triggerEvent(name, data); return this.parent?.triggerEvent(name, data);
} }
handleEventInChildren(name: string, data: unknown = {}) { handleEventInChildren<T extends EventNames>(name: T, data: EventData<T>): Promise<unknown[] | unknown> | null {
const promises = []; const promises: Promise<unknown>[] = [];
for (const child of this.children) { for (const child of this.children) {
const ret = child.handleEvent(name, data); const ret = child.handleEvent(name, data) as Promise<void>;
if (ret) { if (ret) {
promises.push(ret); promises.push(ret);

View File

@ -10,7 +10,7 @@ import bundleService from "../services/bundle.js";
import froca from "../services/froca.js"; import froca from "../services/froca.js";
import linkService from "../services/link.js"; import linkService from "../services/link.js";
import { t } from "../services/i18n.js"; import { t } from "../services/i18n.js";
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
// TODO: Move somewhere else nicer. // TODO: Move somewhere else nicer.
export type SqlExecuteResults = unknown[]; export type SqlExecuteResults = unknown[];

View File

@ -8,7 +8,7 @@ import froca from "../services/froca.js";
import hoistedNoteService from "../services/hoisted_note.js"; import hoistedNoteService from "../services/hoisted_note.js";
import options from "../services/options.js"; import options from "../services/options.js";
import type { ViewScope } from "../services/link.js"; import type { ViewScope } from "../services/link.js";
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
interface SetNoteOpts { interface SetNoteOpts {
triggerSwitchEvent?: unknown; triggerSwitchEvent?: unknown;

View File

@ -5,8 +5,8 @@ import froca from "../services/froca.js";
import protectedSessionHolder from "../services/protected_session_holder.js"; import protectedSessionHolder from "../services/protected_session_holder.js";
import cssClassManager from "../services/css_class_manager.js"; import cssClassManager from "../services/css_class_manager.js";
import type { Froca } from "../services/froca-interface.js"; import type { Froca } from "../services/froca-interface.js";
import FAttachment from "./fattachment.js"; import type FAttachment from "./fattachment.js";
import FAttribute, { type AttributeType } from "./fattribute.js"; import type { default as FAttribute, AttributeType } from "./fattribute.js";
import utils from "../services/utils.js"; import utils from "../services/utils.js";
const LABEL = "label"; const LABEL = "label";
@ -35,7 +35,7 @@ const NOTE_TYPE_ICONS = {
* end user. Those types should be used only for checking against, they are * end user. Those types should be used only for checking against, they are
* not for direct use. * not for direct use.
*/ */
type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code"; type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap";
interface NotePathRecord { interface NotePathRecord {
isArchived: boolean; isArchived: boolean;

View File

@ -27,7 +27,7 @@ import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolb
import SidebarContainer from "../widgets/mobile_widgets/sidebar_container.js"; import SidebarContainer from "../widgets/mobile_widgets/sidebar_container.js";
import AboutDialog from "../widgets/dialogs/about.js"; import AboutDialog from "../widgets/dialogs/about.js";
import HelpDialog from "../widgets/dialogs/help.js"; import HelpDialog from "../widgets/dialogs/help.js";
import AppContext from "../components/app_context.js"; import type AppContext from "../components/app_context.js";
import TabRowWidget from "../widgets/tab_row.js"; import TabRowWidget from "../widgets/tab_row.js";
import JumpToNoteDialog from "../widgets/dialogs/jump_to_note.js"; import JumpToNoteDialog from "../widgets/dialogs/jump_to_note.js";

View File

@ -5,7 +5,7 @@ import dialogService from "../services/dialog.js";
import server from "../services/server.js"; import server from "../services/server.js";
import { t } from "../services/i18n.js"; import { t } from "../services/i18n.js";
import type { SelectMenuItemEventListener } from "../components/events.js"; import type { SelectMenuItemEventListener } from "../components/events.js";
import NoteTreeWidget from "../widgets/note_tree.js"; import type NoteTreeWidget from "../widgets/note_tree.js";
import type { FilteredCommandNames, ContextMenuCommandData } from "../components/app_context.js"; import type { FilteredCommandNames, ContextMenuCommandData } from "../components/app_context.js";
type LauncherCommandNames = FilteredCommandNames<ContextMenuCommandData>; type LauncherCommandNames = FilteredCommandNames<ContextMenuCommandData>;
@ -58,7 +58,7 @@ export default class LauncherContextMenu implements SelectMenuItemEventListener<
{ title: t("launcher_context_menu.reset"), command: "resetLauncher", uiIcon: "bx bx-reset destructive-action-icon", enabled: canBeReset } { title: t("launcher_context_menu.reset"), command: "resetLauncher", uiIcon: "bx bx-reset destructive-action-icon", enabled: canBeReset }
]; ];
return items.filter((row) => row !== null); return items.filter((row) => row !== null) as MenuItem<LauncherCommandNames>[];
} }
async selectMenuItemHandler({ command }: MenuCommandItem<LauncherCommandNames>) { async selectMenuItemHandler({ command }: MenuCommandItem<LauncherCommandNames>) {

View File

@ -9,8 +9,8 @@ import server from "../services/server.js";
import toastService from "../services/toast.js"; import toastService from "../services/toast.js";
import dialogService from "../services/dialog.js"; import dialogService from "../services/dialog.js";
import { t } from "../services/i18n.js"; import { t } from "../services/i18n.js";
import NoteTreeWidget from "../widgets/note_tree.js"; import type NoteTreeWidget from "../widgets/note_tree.js";
import FAttachment from "../entities/fattachment.js"; import type FAttachment from "../entities/fattachment.js";
import type { SelectMenuItemEventListener } from "../components/events.js"; import type { SelectMenuItemEventListener } from "../components/events.js";
// TODO: Deduplicate once client/server is well split. // TODO: Deduplicate once client/server is well split.
@ -196,7 +196,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
enabled: notSearch && noSelectedNotes enabled: notSearch && noSelectedNotes
} }
]; ];
return items.filter((row) => row !== null); return items.filter((row) => row !== null) as MenuItem<TreeCommandNames>[];
} }
async selectMenuItemHandler({ command, type, templateNoteId }: MenuCommandItem<TreeCommandNames>) { async selectMenuItemHandler({ command, type, templateNoteId }: MenuCommandItem<TreeCommandNames>) {

View File

@ -1,7 +1,7 @@
import ws from "./ws.js"; import ws from "./ws.js";
import froca from "./froca.js"; import froca from "./froca.js";
import FAttribute from "../entities/fattribute.js"; import type FAttribute from "../entities/fattribute.js";
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
async function renderAttribute(attribute: FAttribute, renderIsInheritable: boolean) { async function renderAttribute(attribute: FAttribute, renderIsInheritable: boolean) {
const isInheritable = renderIsInheritable && attribute.isInheritable ? `(inheritable)` : ""; const isInheritable = renderIsInheritable && attribute.isInheritable ? `(inheritable)` : "";

View File

@ -1,6 +1,6 @@
import server from "./server.js"; import server from "./server.js";
import froca from "./froca.js"; import froca from "./froca.js";
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
import type { AttributeRow } from "./load_results.js"; import type { AttributeRow } from "./load_results.js";
async function addLabel(noteId: string, name: string, value: string = "") { async function addLabel(noteId: string, name: string, value: string = "") {

View File

@ -14,7 +14,7 @@ import AddLabelBulkAction from "../widgets/bulk_actions/label/add_label.js";
import AddRelationBulkAction from "../widgets/bulk_actions/relation/add_relation.js"; import AddRelationBulkAction from "../widgets/bulk_actions/relation/add_relation.js";
import RenameNoteBulkAction from "../widgets/bulk_actions/note/rename_note.js"; import RenameNoteBulkAction from "../widgets/bulk_actions/note/rename_note.js";
import { t } from "./i18n.js"; import { t } from "./i18n.js";
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
const ACTION_GROUPS = [ const ACTION_GROUPS = [
{ {

View File

@ -1,8 +1,8 @@
import FAttachment from "../entities/fattachment.js"; import type FAttachment from "../entities/fattachment.js";
import FAttribute from "../entities/fattribute.js"; import type FAttribute from "../entities/fattribute.js";
import FBlob from "../entities/fblob.js"; import type FBlob from "../entities/fblob.js";
import FBranch from "../entities/fbranch.js"; import type FBranch from "../entities/fbranch.js";
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
export interface Froca { export interface Froca {
notes: Record<string, FNote>; notes: Record<string, FNote>;

View File

@ -6,7 +6,7 @@ import noteAttributeCache from "./note_attribute_cache.js";
import FBranch, { type FBranchRow } from "../entities/fbranch.js"; import FBranch, { type FBranchRow } from "../entities/fbranch.js";
import FAttribute, { type FAttributeRow } from "../entities/fattribute.js"; import FAttribute, { type FAttributeRow } from "../entities/fattribute.js";
import FAttachment, { type FAttachmentRow } from "../entities/fattachment.js"; import FAttachment, { type FAttachmentRow } from "../entities/fattachment.js";
import FNote, { type FNoteRow } from "../entities/fnote.js"; import type { default as FNote, FNoteRow } from "../entities/fnote.js";
import type { EntityChange } from "../server_types.js"; import type { EntityChange } from "../server_types.js";
async function processEntityChanges(entityChanges: EntityChange[]) { async function processEntityChanges(entityChanges: EntityChange[]) {

View File

@ -15,11 +15,11 @@ import BasicWidget from "../widgets/basic_widget.js";
import SpacedUpdate from "./spaced_update.js"; import SpacedUpdate from "./spaced_update.js";
import shortcutService from "./shortcuts.js"; import shortcutService from "./shortcuts.js";
import dialogService from "./dialog.js"; import dialogService from "./dialog.js";
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
import { t } from "./i18n.js"; import { t } from "./i18n.js";
import NoteContext from "../components/note_context.js"; import type NoteContext from "../components/note_context.js";
import NoteDetailWidget from "../widgets/note_detail.js"; import type NoteDetailWidget from "../widgets/note_detail.js";
import Component from "../components/component.js"; import type Component from "../components/component.js";
/** /**
* A whole number * A whole number
@ -463,7 +463,7 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig
await ws.waitForMaxKnownEntityChangeId(); await ws.waitForMaxKnownEntityChangeId();
await appContext.tabManager.getActiveContext().setNote(notePath); await appContext.tabManager.getActiveContext().setNote(notePath);
await appContext.triggerEvent("focusAndSelectTitle"); await appContext.triggerEvent("focusAndSelectTitle", {});
}; };
this.openTabWithNote = async (notePath, activate) => { this.openTabWithNote = async (notePath, activate) => {
@ -472,7 +472,7 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig
await appContext.tabManager.openTabWithNoteWithHoisting(notePath, { activate }); await appContext.tabManager.openTabWithNoteWithHoisting(notePath, { activate });
if (activate) { if (activate) {
await appContext.triggerEvent("focusAndSelectTitle"); await appContext.triggerEvent("focusAndSelectTitle", {});
} }
}; };
@ -485,7 +485,7 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig
await appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath }); await appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath });
if (activate) { if (activate) {
await appContext.triggerEvent("focusAndSelectTitle"); await appContext.triggerEvent("focusAndSelectTitle", {});
} }
}; };

View File

@ -2,7 +2,7 @@ import appContext from "../components/app_context.js";
import treeService, { type Node } from "./tree.js"; import treeService, { type Node } from "./tree.js";
import dialogService from "./dialog.js"; import dialogService from "./dialog.js";
import froca from "./froca.js"; import froca from "./froca.js";
import NoteContext from "../components/note_context.js"; import type NoteContext from "../components/note_context.js";
import { t } from "./i18n.js"; import { t } from "./i18n.js";
function getHoistedNoteId() { function getHoistedNoteId() {

View File

@ -1,7 +1,7 @@
import server from "./server.js"; import server from "./server.js";
import appContext, { type CommandNames } from "../components/app_context.js"; import appContext, { type CommandNames } from "../components/app_context.js";
import shortcutService from "./shortcuts.js"; import shortcutService from "./shortcuts.js";
import Component from "../components/component.js"; import type Component from "../components/component.js";
const keyboardActionRepo: Record<string, Action> = {}; const keyboardActionRepo: Record<string, Action> = {};

View File

@ -96,10 +96,6 @@ const I18NEXT: Library = {
js: ["node_modules/i18next/i18next.min.js", "node_modules/i18next-http-backend/i18nextHttpBackend.min.js"] js: ["node_modules/i18next/i18next.min.js", "node_modules/i18next-http-backend/i18nextHttpBackend.min.js"]
}; };
const MIND_ELIXIR: Library = {
js: ["node_modules/mind-elixir/dist/MindElixir.iife.js", "node_modules/@mind-elixir/node-menu/dist/node-menu.umd.cjs"]
};
const HIGHLIGHT_JS: Library = { const HIGHLIGHT_JS: Library = {
js: () => { js: () => {
const mimeTypes = mimeTypesService.getMimeTypes(); const mimeTypes = mimeTypesService.getMimeTypes();
@ -219,6 +215,5 @@ export default {
EXCALIDRAW, EXCALIDRAW,
MARKJS, MARKJS,
I18NEXT, I18NEXT,
MIND_ELIXIR,
HIGHLIGHT_JS HIGHLIGHT_JS
}; };

View File

@ -1,4 +1,4 @@
import FAttribute from "../entities/fattribute.js"; import type FAttribute from "../entities/fattribute.js";
/** /**
* The purpose of this class is to cache the list of attributes for notes. * The purpose of this class is to cache the list of attributes for notes.

View File

@ -6,8 +6,8 @@ import froca from "./froca.js";
import treeService from "./tree.js"; import treeService from "./tree.js";
import toastService from "./toast.js"; import toastService from "./toast.js";
import { t } from "./i18n.js"; import { t } from "./i18n.js";
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
import FBranch from "../entities/fbranch.js"; import type FBranch from "../entities/fbranch.js";
import type { ChooseNoteTypeResponse } from "../widgets/dialogs/note_type_chooser.js"; import type { ChooseNoteTypeResponse } from "../widgets/dialogs/note_type_chooser.js";
interface CreateNoteOpts { interface CreateNoteOpts {

View File

@ -5,7 +5,7 @@ import attributeRenderer from "./attribute_renderer.js";
import libraryLoader from "./library_loader.js"; import libraryLoader from "./library_loader.js";
import treeService from "./tree.js"; import treeService from "./tree.js";
import utils from "./utils.js"; import utils from "./utils.js";
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
const TPL = ` const TPL = `
<div class="note-list"> <div class="note-list">

View File

@ -5,7 +5,7 @@ import utils from "./utils.js";
import attributeRenderer from "./attribute_renderer.js"; import attributeRenderer from "./attribute_renderer.js";
import contentRenderer from "./content_renderer.js"; import contentRenderer from "./content_renderer.js";
import appContext from "../components/app_context.js"; import appContext from "../components/app_context.js";
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
import { t } from "./i18n.js"; import { t } from "./i18n.js";
function setupGlobalTooltip() { function setupGlobalTooltip() {

View File

@ -74,9 +74,9 @@ ws.subscribeToMessages(async (message) => {
if (message.type === "protectedSessionLogin") { if (message.type === "protectedSessionLogin") {
await reloadData(); await reloadData();
await appContext.triggerEvent("frocaReloaded"); await appContext.triggerEvent("frocaReloaded", {});
appContext.triggerEvent("protectedSessionStarted"); appContext.triggerEvent("protectedSessionStarted", {});
appContext.triggerCommand("closeProtectedSessionPasswordDialog"); appContext.triggerCommand("closeProtectedSessionPasswordDialog");

View File

@ -1,4 +1,4 @@
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
import server from "./server.js"; import server from "./server.js";
function enableProtectedSession() { function enableProtectedSession() {

View File

@ -1,6 +1,6 @@
import server from "./server.js"; import server from "./server.js";
import bundleService, { type Bundle } from "./bundle.js"; import bundleService, { type Bundle } from "./bundle.js";
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
async function render(note: FNote, $el: JQuery<HTMLElement>) { async function render(note: FNote, $el: JQuery<HTMLElement>) {
const relations = note.getRelations("renderNote"); const relations = note.getRelations("renderNote");

View File

@ -20,3 +20,7 @@ declare module "draggabilly" {
destroy(); destroy();
} }
} }
declare module '@mind-elixir/node-menu' {
export default mindmap;
}

View File

@ -3,17 +3,17 @@ import NoteContextAwareWidget from "../note_context_aware_widget.js";
import noteAutocompleteService from "../../services/note_autocomplete.js"; import noteAutocompleteService from "../../services/note_autocomplete.js";
import server from "../../services/server.js"; import server from "../../services/server.js";
import contextMenuService from "../../menus/context_menu.js"; import contextMenuService from "../../menus/context_menu.js";
import attributeParser from "../../services/attribute_parser.js"; import attributeParser, { type Attribute } from "../../services/attribute_parser.js";
import libraryLoader from "../../services/library_loader.js"; import libraryLoader from "../../services/library_loader.js";
import froca from "../../services/froca.js"; import froca from "../../services/froca.js";
import attributeRenderer from "../../services/attribute_renderer.js"; import attributeRenderer from "../../services/attribute_renderer.js";
import noteCreateService from "../../services/note_create.js"; import noteCreateService from "../../services/note_create.js";
import attributeService from "../../services/attributes.js"; import attributeService from "../../services/attributes.js";
import linkService from "../../services/link.js"; import linkService from "../../services/link.js";
import AttributeDetailWidget from "./attribute_detail.js"; import type AttributeDetailWidget from "./attribute_detail.js";
import type { CommandData, EventData, EventListener, FilteredCommandNames } from "../../components/app_context.js"; import type { CommandData, EventData, EventListener, FilteredCommandNames } from "../../components/app_context.js";
import FAttribute, { type AttributeType } from "../../entities/fattribute.js"; import type { default as FAttribute, AttributeType } from "../../entities/fattribute.js";
import FNote from "../../entities/fnote.js"; import type FNote from "../../entities/fnote.js";
const HELP_TEXT = ` const HELP_TEXT = `
<p>${t("attribute_editor.help_text_body1")}</p> <p>${t("attribute_editor.help_text_body1")}</p>
@ -417,7 +417,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem
return null; return null;
} }
let matchedAttr = null; let matchedAttr: Attribute | null = null;
for (const attr of parsedAttrs) { for (const attr of parsedAttrs) {
if (attr.startIndex && clickIndex > attr.startIndex && attr.endIndex && clickIndex <= attr.endIndex) { if (attr.startIndex && clickIndex > attr.startIndex && attr.endIndex && clickIndex <= attr.endIndex) {

View File

@ -2,7 +2,7 @@ import { t } from "../../services/i18n.js";
import server from "../../services/server.js"; import server from "../../services/server.js";
import ws from "../../services/ws.js"; import ws from "../../services/ws.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
import FAttribute from "../../entities/fattribute.js"; import type FAttribute from "../../entities/fattribute.js";
interface ActionDefinition { interface ActionDefinition {
script: string; script: string;

View File

@ -1,4 +1,4 @@
import Component, { TypedComponent } from "../../components/component.js"; import type { default as Component, TypedComponent } from "../../components/component.js";
import BasicWidget, { TypedBasicWidget } from "../basic_widget.js"; import BasicWidget, { TypedBasicWidget } from "../basic_widget.js";
export default class Container<T extends TypedComponent<any>> extends TypedBasicWidget<T> { export default class Container<T extends TypedComponent<any>> extends TypedBasicWidget<T> {

View File

@ -1,4 +1,4 @@
import { TypedComponent } from "../../components/component.js"; import type { TypedComponent } from "../../components/component.js";
import Container from "./container.js"; import Container from "./container.js";
export type FlexDirection = "row" | "column"; export type FlexDirection = "row" | "column";

View File

@ -11,7 +11,7 @@ import utils from "../../services/utils.js";
import TodayLauncher from "../buttons/launcher/today_launcher.js"; import TodayLauncher from "../buttons/launcher/today_launcher.js";
import HistoryNavigationButton from "../buttons/history_navigation.js"; import HistoryNavigationButton from "../buttons/history_navigation.js";
import QuickSearchLauncherWidget from "../quick_search_launcher.js"; import QuickSearchLauncherWidget from "../quick_search_launcher.js";
import FNote from "../../entities/fnote.js"; import type FNote from "../../entities/fnote.js";
import type { CommandNames } from "../../components/app_context.js"; import type { CommandNames } from "../../components/app_context.js";
interface InnerWidget extends BasicWidget { interface InnerWidget extends BasicWidget {

View File

@ -51,7 +51,7 @@ export default class LauncherContainer extends FlexContainer<LauncherWidget> {
this.$widget.empty(); this.$widget.empty();
this.renderChildren(); this.renderChildren();
await this.handleEventInChildren("initialRenderComplete"); await this.handleEventInChildren("initialRenderComplete", {});
const activeContext = appContext.tabManager.getActiveContext(); const activeContext = appContext.tabManager.getActiveContext();

View File

@ -1,6 +1,7 @@
import FlexContainer from "./flex_container.js"; import FlexContainer from "./flex_container.js";
import splitService from "../../services/resizer.js"; import splitService from "../../services/resizer.js";
import RightPanelWidget from "../right_panel_widget.js"; import type RightPanelWidget from "../right_panel_widget.js";
import type { EventData, EventNames } from "../../components/app_context.js";
export default class RightPaneContainer extends FlexContainer<RightPanelWidget> { export default class RightPaneContainer extends FlexContainer<RightPanelWidget> {
private rightPaneHidden: boolean; private rightPaneHidden: boolean;
@ -19,7 +20,7 @@ export default class RightPaneContainer extends FlexContainer<RightPanelWidget>
return super.isEnabled() && !this.rightPaneHidden && this.children.length > 0 && !!this.children.find((ch) => ch.isEnabled() && ch.canBeShown()); return super.isEnabled() && !this.rightPaneHidden && this.children.length > 0 && !!this.children.find((ch) => ch.isEnabled() && ch.canBeShown());
} }
handleEventInChildren(name: string, data: unknown) { handleEventInChildren<T extends EventNames>(name: T, data: EventData<T>): Promise<unknown[] | unknown> | null {
const promise = super.handleEventInChildren(name, data); const promise = super.handleEventInChildren(name, data);
if (["activeContextChanged", "noteSwitchedAndActivated", "noteSwitched"].includes(name)) { if (["activeContextChanged", "noteSwitchedAndActivated", "noteSwitched"].includes(name)) {

View File

@ -12,7 +12,7 @@ import options from "../services/options.js";
import OnClickButtonWidget from "./buttons/onclick_button.js"; import OnClickButtonWidget from "./buttons/onclick_button.js";
import appContext, { type EventData } from "../components/app_context.js"; import appContext, { type EventData } from "../components/app_context.js";
import libraryLoader from "../services/library_loader.js"; import libraryLoader from "../services/library_loader.js";
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
const TPL = `<div class="highlights-list-widget"> const TPL = `<div class="highlights-list-widget">
<style> <style>

View File

@ -1,6 +1,6 @@
import type { EventData } from "../../components/app_context.js"; import type { EventData } from "../../components/app_context.js";
import type { Screen } from "../../components/mobile_screen_switcher.js"; import type { Screen } from "../../components/mobile_screen_switcher.js";
import BasicWidget from "../basic_widget.js"; import type BasicWidget from "../basic_widget.js";
import FlexContainer, { type FlexDirection } from "../containers/flex_container.js"; import FlexContainer, { type FlexDirection } from "../containers/flex_container.js";
const DRAG_STATE_NONE = 0; const DRAG_STATE_NONE = 0;

View File

@ -1,7 +1,7 @@
import BasicWidget from "./basic_widget.js"; import BasicWidget from "./basic_widget.js";
import appContext, { type EventData } from "../components/app_context.js"; import appContext, { type EventData } from "../components/app_context.js";
import FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
import NoteContext from "../components/note_context.js"; import type NoteContext from "../components/note_context.js";
/** /**
* This widget allows for changing and updating depending on the active note. * This widget allows for changing and updating depending on the active note.

View File

@ -1,5 +1,5 @@
import BasicWidget from "./basic_widget.js"; import type BasicWidget from "./basic_widget.js";
import AbstractButtonWidget from "./buttons/abstract_button.js"; import type AbstractButtonWidget from "./buttons/abstract_button.js";
import NoteContextAwareWidget from "./note_context_aware_widget.js"; import NoteContextAwareWidget from "./note_context_aware_widget.js";
const WIDGET_TPL = ` const WIDGET_TPL = `

View File

@ -165,13 +165,13 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
this.watchdog.on("stateChange", () => { this.watchdog.on("stateChange", () => {
const currentState = this.watchdog.state; const currentState = this.watchdog.state;
logInfo(`CKEditor state changed to ${currentState}`);
if (!["crashed", "crashedPermanently"].includes(currentState)) { if (!["crashed", "crashedPermanently"].includes(currentState)) {
return; return;
} }
console.log(`CKEditor changed to ${currentState}`); logInfo(`CKEditor crash logs: ${JSON.stringify(this.watchdog.crashes)}`);
this.watchdog.crashes.forEach((crashInfo) => console.log(crashInfo)); this.watchdog.crashes.forEach((crashInfo) => console.log(crashInfo));
if (currentState === "crashedPermanently") { if (currentState === "crashedPermanently") {
@ -182,6 +182,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
}); });
this.watchdog.setCreator(async (elementOrData, editorConfig) => { this.watchdog.setCreator(async (elementOrData, editorConfig) => {
logInfo("Creating new CKEditor");
const extraOpts = {}; const extraOpts = {};
if (isClassicEditor) { if (isClassicEditor) {
extraOpts.toolbar = { extraOpts.toolbar = {

View File

@ -1,6 +1,11 @@
import libraryLoader from "../../services/library_loader.js";
import TypeWidget from "./type_widget.js"; import TypeWidget from "./type_widget.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
import MindElixir, { type MindElixirCtor } from "mind-elixir";
import nodeMenu from "@mind-elixir/node-menu";
import type FNote from "../../entities/fnote.js";
import type { EventData } from "../../components/app_context.js";
const NEW_TOPIC_NAME = "";
const TPL = ` const TPL = `
<div class="note-detail-mind-map note-detail-printable"> <div class="note-detail-mind-map note-detail-printable">
@ -137,6 +142,11 @@ const TPL = `
`; `;
export default class MindMapWidget extends TypeWidget { export default class MindMapWidget extends TypeWidget {
private $content!: JQuery<HTMLElement>;
private triggeredByUserOperation?: boolean;
private mind?: ReturnType<MindElixirCtor["new"]>;
static getType() { static getType() {
return "mindMap"; return "mindMap";
} }
@ -163,16 +173,12 @@ export default class MindMapWidget extends TypeWidget {
super.doRender(); super.doRender();
} }
async doRefresh(note) { async doRefresh(note: FNote) {
if (this.triggeredByUserOperation) { if (this.triggeredByUserOperation) {
this.triggeredByUserOperation = false; this.triggeredByUserOperation = false;
return; return;
} }
if (!window.MindElixir) {
await libraryLoader.requireLibrary(libraryLoader.MIND_ELIXIR);
}
this.#initLibrary(); this.#initLibrary();
await this.#loadData(note); await this.#loadData(note);
} }
@ -181,12 +187,14 @@ export default class MindMapWidget extends TypeWidget {
this.triggeredByUserOperation = false; this.triggeredByUserOperation = false;
} }
async #loadData(note) { async #loadData(note: FNote) {
const blob = await note.getBlob(); const blob = await note.getBlob();
const content = blob.getJsonContent() || MindElixir.new(); const content = blob?.getJsonContent() || MindElixir.new(NEW_TOPIC_NAME);
this.mind.refresh(content); if (this.mind) {
this.mind.toCenter(); this.mind.refresh(content);
this.mind.toCenter();
}
} }
#initLibrary() { #initLibrary() {
@ -194,11 +202,12 @@ export default class MindMapWidget extends TypeWidget {
el: this.$content[0], el: this.$content[0],
direction: MindElixir.LEFT direction: MindElixir.LEFT
}); });
mind.install(window["@mind-elixir/node-menu"]); mind.install(nodeMenu);
this.mind = mind; this.mind = mind;
mind.init(MindElixir.new()); mind.init(MindElixir.new(NEW_TOPIC_NAME));
mind.bus.addListener("operation", (operation) => { // TODO: See why the typeof mindmap is not correct.
mind.bus.addListener("operation", (operation: { name: string }) => {
this.triggeredByUserOperation = true; this.triggeredByUserOperation = true;
if (operation.name !== "beginEdit") { if (operation.name !== "beginEdit") {
this.spacedUpdate.scheduleUpdate(); this.spacedUpdate.scheduleUpdate();
@ -237,14 +246,14 @@ export default class MindMapWidget extends TypeWidget {
return await this.mind.exportSvg().text(); return await this.mind.exportSvg().text();
} }
async entitiesReloadedEvent({ loadResults }) { async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded"> ) {
if (loadResults.isNoteReloaded(this.noteId)) { if (this.noteId && loadResults.isNoteReloaded(this.noteId)) {
this.refresh(); this.refresh();
} }
} }
async exportSvgEvent({ ntxId }) { async exportSvgEvent({ ntxId }: EventData<"exportSvg">) {
if (!this.isNoteContext(ntxId) || this.note.type !== "mindMap") { if (!this.isNoteContext(ntxId) || this.note?.type !== "mindMap") {
return; return;
} }

View File

@ -1,6 +1,6 @@
import type { FilterOptionsByType, OptionDefinitions, OptionMap, OptionNames } from "../../../../../services/options_interface.js"; import type { FilterOptionsByType, OptionDefinitions, OptionMap, OptionNames } from "../../../../../services/options_interface.js";
import type { EventData, EventListener } from "../../../components/app_context.js"; import type { EventData, EventListener } from "../../../components/app_context.js";
import FNote from "../../../entities/fnote.js"; import type FNote from "../../../entities/fnote.js";
import { t } from "../../../services/i18n.js"; import { t } from "../../../services/i18n.js";
import server from "../../../services/server.js"; import server from "../../../services/server.js";
import toastService from "../../../services/toast.js"; import toastService from "../../../services/toast.js";

View File

@ -1,7 +1,13 @@
import NoteContextAwareWidget from "../note_context_aware_widget.js"; import NoteContextAwareWidget from "../note_context_aware_widget.js";
import appContext from "../../components/app_context.js"; import appContext, { type EventData, type EventNames } from "../../components/app_context.js";
import type FNote from "../../entities/fnote.js";
import type NoteDetailWidget from "../note_detail.js";
import type SpacedUpdate from "../../services/spaced_update.js";
export default abstract class TypeWidget extends NoteContextAwareWidget {
protected spacedUpdate!: SpacedUpdate;
export default class TypeWidget extends NoteContextAwareWidget {
// for overriding // for overriding
static getType() {} static getType() {}
@ -11,12 +17,11 @@ export default class TypeWidget extends NoteContextAwareWidget {
return super.doRender(); return super.doRender();
} }
/** @param {FNote} note */ abstract doRefresh(note: FNote | null | undefined): Promise<void>;
async doRefresh(note) {}
async refresh() { async refresh() {
const thisWidgetType = this.constructor.getType(); const thisWidgetType = (this.constructor as any).getType();
const noteWidgetType = await this.parent.getWidgetType(); const noteWidgetType = await (this.parent as NoteDetailWidget).getWidgetType();
if (thisWidgetType !== noteWidgetType) { if (thisWidgetType !== noteWidgetType) {
this.toggleInt(false); this.toggleInt(false);
@ -27,7 +32,7 @@ export default class TypeWidget extends NoteContextAwareWidget {
await this.doRefresh(this.note); await this.doRefresh(this.note);
this.triggerEvent("noteDetailRefreshed", { ntxId: this.noteContext.ntxId }); this.triggerEvent("noteDetailRefreshed", { ntxId: this.noteContext?.ntxId });
} }
} }
@ -40,7 +45,7 @@ export default class TypeWidget extends NoteContextAwareWidget {
focus() {} focus() {}
async readOnlyTemporarilyDisabledEvent({ noteContext }) { async readOnlyTemporarilyDisabledEvent({ noteContext }: EventData<"readOnlyTemporarilyDisabled">) {
if (this.isNoteContext(noteContext.ntxId)) { if (this.isNoteContext(noteContext.ntxId)) {
await this.refresh(); await this.refresh();
@ -49,10 +54,10 @@ export default class TypeWidget extends NoteContextAwareWidget {
} }
// events should be propagated manually to the children widgets // events should be propagated manually to the children widgets
handleEventInChildren(name, data) { handleEventInChildren<T extends EventNames>(name: T, data: EventData<T>) {
if (["activeContextChanged", "setNoteContext"].includes(name)) { if (["activeContextChanged", "setNoteContext"].includes(name)) {
// won't trigger .refresh(); // won't trigger .refresh();
return super.handleEventInChildren("setNoteContext", data); return super.handleEventInChildren("setNoteContext", data as EventData<"activeContextChanged">);
} else if (name === "entitiesReloaded") { } else if (name === "entitiesReloaded") {
return super.handleEventInChildren(name, data); return super.handleEventInChildren(name, data);
} else { } else {

View File

@ -16,7 +16,7 @@ import path from "path";
import htmlSanitizer from "../../services/html_sanitizer.js"; import htmlSanitizer from "../../services/html_sanitizer.js";
import attributeFormatter from "../../services/attribute_formatter.js"; import attributeFormatter from "../../services/attribute_formatter.js";
import jsdom from "jsdom"; import jsdom from "jsdom";
import BNote from "../../becca/entities/bnote.js"; import type BNote from "../../becca/entities/bnote.js";
import ValidationError from "../../errors/validation_error.js"; import ValidationError from "../../errors/validation_error.js";
const { JSDOM } = jsdom; const { JSDOM } = jsdom;

View File

@ -12,8 +12,8 @@ import ws from "../../services/ws.js";
import becca from "../../becca/becca.js"; import becca from "../../becca/becca.js";
import ValidationError from "../../errors/validation_error.js"; import ValidationError from "../../errors/validation_error.js";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import BNote from "../../becca/entities/bnote.js"; import type BNote from "../../becca/entities/bnote.js";
import BAttachment from "../../becca/entities/battachment.js"; import type BAttachment from "../../becca/entities/battachment.js";
function updateFile(req: Request) { function updateFile(req: Request) {
const note = becca.getNoteOrThrow(req.params.noteId); const note = becca.getNoteOrThrow(req.params.noteId);

View File

@ -4,8 +4,8 @@ import imageService from "../../services/image.js";
import becca from "../../becca/becca.js"; import becca from "../../becca/becca.js";
import fs from "fs"; import fs from "fs";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import BNote from "../../becca/entities/bnote.js"; import type BNote from "../../becca/entities/bnote.js";
import BRevision from "../../becca/entities/brevision.js"; import type BRevision from "../../becca/entities/brevision.js";
import { RESOURCE_DIR } from "../../services/resource_dir.js"; import { RESOURCE_DIR } from "../../services/resource_dir.js";
function returnImageFromNote(req: Request, res: Response) { function returnImageFromNote(req: Request, res: Response) {

View File

@ -12,7 +12,7 @@ import log from "../../services/log.js";
import TaskContext from "../../services/task_context.js"; import TaskContext from "../../services/task_context.js";
import ValidationError from "../../errors/validation_error.js"; import ValidationError from "../../errors/validation_error.js";
import type { Request } from "express"; import type { Request } from "express";
import BNote from "../../becca/entities/bnote.js"; import type BNote from "../../becca/entities/bnote.js";
async function importNotesToBranch(req: Request) { async function importNotesToBranch(req: Request) {
const { parentNoteId } = req.params; const { parentNoteId } = req.params;

View File

@ -2,8 +2,8 @@
import becca from "../../becca/becca.js"; import becca from "../../becca/becca.js";
import { JSDOM } from "jsdom"; import { JSDOM } from "jsdom";
import BNote from "../../becca/entities/bnote.js"; import type BNote from "../../becca/entities/bnote.js";
import BAttribute from "../../becca/entities/battribute.js"; import type BAttribute from "../../becca/entities/battribute.js";
import type { Request } from "express"; import type { Request } from "express";
function buildDescendantCountMap(noteIdsToCount: string[]) { function buildDescendantCountMap(noteIdsToCount: string[]) {

View File

@ -11,7 +11,7 @@ import becca from "../../becca/becca.js";
import ValidationError from "../../errors/validation_error.js"; import ValidationError from "../../errors/validation_error.js";
import blobService from "../../services/blob.js"; import blobService from "../../services/blob.js";
import type { Request } from "express"; import type { Request } from "express";
import BBranch from "../../becca/entities/bbranch.js"; import type BBranch from "../../becca/entities/bbranch.js";
import type { AttributeRow } from "../../becca/entities/rows.js"; import type { AttributeRow } from "../../becca/entities/rows.js";
function getNote(req: Request) { function getNote(req: Request) {

View File

@ -10,9 +10,9 @@ import becca from "../../becca/becca.js";
import blobService from "../../services/blob.js"; import blobService from "../../services/blob.js";
import eraseService from "../../services/erase.js"; import eraseService from "../../services/erase.js";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import BRevision from "../../becca/entities/brevision.js"; import type BRevision from "../../becca/entities/brevision.js";
import BNote from "../../becca/entities/bnote.js"; import type BNote from "../../becca/entities/bnote.js";
import { type NotePojo } from "../../becca/becca-interface.js"; import type { NotePojo } from "../../becca/becca-interface.js";
interface NotePath { interface NotePath {
noteId: string; noteId: string;

View File

@ -9,7 +9,7 @@ import bulkActionService from "../../services/bulk_actions.js";
import cls from "../../services/cls.js"; import cls from "../../services/cls.js";
import attributeFormatter from "../../services/attribute_formatter.js"; import attributeFormatter from "../../services/attribute_formatter.js";
import ValidationError from "../../errors/validation_error.js"; import ValidationError from "../../errors/validation_error.js";
import SearchResult from "../../services/search/search_result.js"; import type SearchResult from "../../services/search/search_result.js";
function searchFromNote(req: Request): SearchNoteResult { function searchFromNote(req: Request): SearchNoteResult {
const note = becca.getNoteOrThrow(req.params.noteId); const note = becca.getNoteOrThrow(req.params.noteId);

View File

@ -4,7 +4,7 @@ import becca from "../../becca/becca.js";
import log from "../../services/log.js"; import log from "../../services/log.js";
import NotFoundError from "../../errors/not_found_error.js"; import NotFoundError from "../../errors/not_found_error.js";
import type { Request } from "express"; import type { Request } from "express";
import BNote from "../../becca/entities/bnote.js"; import type BNote from "../../becca/entities/bnote.js";
function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set<string>) { function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set<string>) {
const noteIds = new Set(_noteIds); const noteIds = new Set(_noteIds);

View File

@ -3,7 +3,7 @@ import path from "path";
import { fileURLToPath } from "url"; import { fileURLToPath } from "url";
import express from "express"; import express from "express";
import env from "../services/env.js"; import env from "../services/env.js";
import serveStatic from "serve-static"; import type serveStatic from "serve-static";
const persistentCacheStatic = (root: string, options?: serveStatic.ServeStaticOptions<express.Response<any, Record<string, any>>>) => { const persistentCacheStatic = (root: string, options?: serveStatic.ServeStaticOptions<express.Response<any, Record<string, any>>>) => {
if (!env.isDev()) { if (!env.isDev()) {

View File

@ -12,7 +12,7 @@ import packageJson from "../../package.json" with { type: "json" };
import assetPath from "../services/asset_path.js"; import assetPath from "../services/asset_path.js";
import appPath from "../services/app_path.js"; import appPath from "../services/app_path.js";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import BNote from "../becca/entities/bnote.js"; import type BNote from "../becca/entities/bnote.js";
function index(req: Request, res: Response) { function index(req: Request, res: Response) {
const options = optionService.getOptionMap(); const options = optionService.getOptionMap();

View File

@ -6,7 +6,7 @@ import becca from "../becca/becca.js";
import BAttribute from "../becca/entities/battribute.js"; import BAttribute from "../becca/entities/battribute.js";
import attributeFormatter from "./attribute_formatter.js"; import attributeFormatter from "./attribute_formatter.js";
import BUILTIN_ATTRIBUTES from "./builtin_attributes.js"; import BUILTIN_ATTRIBUTES from "./builtin_attributes.js";
import BNote from "../becca/entities/bnote.js"; import type BNote from "../becca/entities/bnote.js";
import type { AttributeRow } from "../becca/entities/rows.js"; import type { AttributeRow } from "../becca/entities/rows.js";
const ATTRIBUTE_TYPES = new Set(["label", "relation"]); const ATTRIBUTE_TYPES = new Set(["label", "relation"]);

View File

@ -23,16 +23,16 @@ import exportService from "./export/zip.js";
import syncMutex from "./sync_mutex.js"; import syncMutex from "./sync_mutex.js";
import backupService from "./backup.js"; import backupService from "./backup.js";
import optionsService from "./options.js"; import optionsService from "./options.js";
import BNote from "../becca/entities/bnote.js"; import type BNote from "../becca/entities/bnote.js";
import AbstractBeccaEntity from "../becca/entities/abstract_becca_entity.js"; import type AbstractBeccaEntity from "../becca/entities/abstract_becca_entity.js";
import BBranch from "../becca/entities/bbranch.js"; import type BBranch from "../becca/entities/bbranch.js";
import BAttribute from "../becca/entities/battribute.js"; import type BAttribute from "../becca/entities/battribute.js";
import BAttachment from "../becca/entities/battachment.js"; import type BAttachment from "../becca/entities/battachment.js";
import BRevision from "../becca/entities/brevision.js"; import type BRevision from "../becca/entities/brevision.js";
import BEtapiToken from "../becca/entities/betapi_token.js"; import type BEtapiToken from "../becca/entities/betapi_token.js";
import BOption from "../becca/entities/boption.js"; import type BOption from "../becca/entities/boption.js";
import type { AttributeRow } from "../becca/entities/rows.js"; import type { AttributeRow } from "../becca/entities/rows.js";
import Becca from "../becca/becca-interface.js"; import type Becca from "../becca/becca-interface.js";
import type { NoteParams } from "./note-interface.js"; import type { NoteParams } from "./note-interface.js";
import type { ApiParams } from "./backend_script_api_interface.js"; import type { ApiParams } from "./backend_script_api_interface.js";

View File

@ -1,6 +1,6 @@
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import AbstractBeccaEntity from "../becca/entities/abstract_becca_entity.js"; import type AbstractBeccaEntity from "../becca/entities/abstract_becca_entity.js";
import BNote from "../becca/entities/bnote.js"; import type BNote from "../becca/entities/bnote.js";
export interface ApiParams { export interface ApiParams {
startNote?: BNote | null; startNote?: BNote | null;

View File

@ -1,6 +1,6 @@
import treeService from "./tree.js"; import treeService from "./tree.js";
import sql from "./sql.js"; import sql from "./sql.js";
import BBranch from "../becca/entities/bbranch.js"; import type BBranch from "../becca/entities/bbranch.js";
function moveBranchToNote(branchToMove: BBranch, targetParentNoteId: string) { function moveBranchToNote(branchToMove: BBranch, targetParentNoteId: string) {
if (branchToMove.parentNoteId === targetParentNoteId) { if (branchToMove.parentNoteId === targetParentNoteId) {

View File

@ -4,7 +4,7 @@ import cloningService from "./cloning.js";
import branchService from "./branches.js"; import branchService from "./branches.js";
import { randomString } from "./utils.js"; import { randomString } from "./utils.js";
import eraseService from "./erase.js"; import eraseService from "./erase.js";
import BNote from "../becca/entities/bnote.js"; import type BNote from "../becca/entities/bnote.js";
interface Action { interface Action {
labelName: string; labelName: string;

View File

@ -2,75 +2,83 @@
/* /*
* This file resolves trilium data path in this order of priority: * This file resolves trilium data path in this order of priority:
* - if TRILIUM_DATA_DIR environment variable exists, then its value is used as the path * - case A) if TRILIUM_DATA_DIR environment variable exists, then its value is used as the path
* - if "trilium-data" dir exists directly in the home dir, then it is used * - case B) if "trilium-data" dir exists directly in the home dir, then it is used
* - based on OS convention, if the "app data directory" exists, we'll use or create "trilium-data" directory there * - case C) based on OS convention, if the "app data directory" exists, we'll use or create "trilium-data" directory there
* - as a fallback if the previous step fails, we'll use home dir * - case D) as a fallback if the previous step fails, we'll use home dir
*/ */
import os from "os"; import os from "os";
import fs from "fs"; import fs from "fs";
import path from "path"; import { join as pathJoin } from "path";
function getAppDataDir() {
let appDataDir = os.homedir(); // fallback if OS is not recognized
if (os.platform() === "win32" && process.env.APPDATA) {
appDataDir = process.env.APPDATA;
} else if (os.platform() === "linux") {
appDataDir = `${os.homedir()}/.local/share`;
} else if (os.platform() === "darwin") {
appDataDir = `${os.homedir()}/Library/Application Support`;
}
if (!fs.existsSync(appDataDir)) {
// expected app data path doesn't exist, let's use fallback
appDataDir = os.homedir();
}
return appDataDir;
}
const DIR_NAME = "trilium-data"; const DIR_NAME = "trilium-data";
const FOLDER_PERMISSIONS = 0o700;
function getTriliumDataDir() { export function getTriliumDataDir(dataDirName: string) {
// case A
if (process.env.TRILIUM_DATA_DIR) { if (process.env.TRILIUM_DATA_DIR) {
if (!fs.existsSync(process.env.TRILIUM_DATA_DIR)) { createDirIfNotExisting(process.env.TRILIUM_DATA_DIR);
fs.mkdirSync(process.env.TRILIUM_DATA_DIR, 0o700);
}
return process.env.TRILIUM_DATA_DIR; return process.env.TRILIUM_DATA_DIR;
} }
const homePath = os.homedir() + path.sep + DIR_NAME; // case B
const homePath = pathJoin(os.homedir(), dataDirName);
if (fs.existsSync(homePath)) { if (fs.existsSync(homePath)) {
return homePath; return homePath;
} }
const appDataPath = getAppDataDir() + path.sep + DIR_NAME; // case C
const platformAppDataDir = getPlatformAppDataDir(os.platform(), process.env.APPDATA);
if (!fs.existsSync(appDataPath)) { if (platformAppDataDir && fs.existsSync(platformAppDataDir)) {
fs.mkdirSync(appDataPath, 0o700); const appDataDirPath = pathJoin(platformAppDataDir, dataDirName);
createDirIfNotExisting(appDataDirPath);
return appDataDirPath;
} }
return appDataPath; // case D
createDirIfNotExisting(homePath);
return homePath;
} }
const TRILIUM_DATA_DIR = getTriliumDataDir(); export function getDataDirs(TRILIUM_DATA_DIR: string) {
const DIR_SEP = TRILIUM_DATA_DIR + path.sep; const dataDirs = {
TRILIUM_DATA_DIR: TRILIUM_DATA_DIR,
DOCUMENT_PATH: process.env.TRILIUM_DOCUMENT_PATH || pathJoin(TRILIUM_DATA_DIR, "document.db"),
BACKUP_DIR: process.env.TRILIUM_BACKUP_DIR || pathJoin(TRILIUM_DATA_DIR, "backup"),
LOG_DIR: process.env.TRILIUM_LOG_DIR || pathJoin(TRILIUM_DATA_DIR, "log"),
ANONYMIZED_DB_DIR: process.env.TRILIUM_ANONYMIZED_DB_DIR || pathJoin(TRILIUM_DATA_DIR, "anonymized-db"),
CONFIG_INI_PATH: process.env.TRILIUM_CONFIG_INI_PATH || pathJoin(TRILIUM_DATA_DIR, "config.ini")
} as const;
const DOCUMENT_PATH = process.env.TRILIUM_DOCUMENT_PATH || `${DIR_SEP}document.db`; Object.freeze(dataDirs);
const BACKUP_DIR = process.env.TRILIUM_BACKUP_DIR || `${DIR_SEP}backup`; return dataDirs;
const LOG_DIR = process.env.TRILIUM_LOG_DIR || `${DIR_SEP}log`; }
const ANONYMIZED_DB_DIR = process.env.TRILIUM_ANONYMIZED_DB_DIR || `${DIR_SEP}anonymized-db`;
const CONFIG_INI_PATH = process.env.TRILIUM_CONFIG_INI_PATH || `${DIR_SEP}config.ini`;
export default { export function getPlatformAppDataDir(platform: ReturnType<typeof os.platform>, ENV_APPDATA_DIR: string | undefined = process.env.APPDATA) {
TRILIUM_DATA_DIR, switch (true) {
DOCUMENT_PATH, case platform === "win32" && !!ENV_APPDATA_DIR:
BACKUP_DIR, return ENV_APPDATA_DIR;
LOG_DIR,
ANONYMIZED_DB_DIR, case platform === "linux":
CONFIG_INI_PATH return `${os.homedir()}/.local/share`;
};
case platform === "darwin":
return `${os.homedir()}/Library/Application Support`;
default:
// if OS is not recognized
return null;
}
}
function createDirIfNotExisting(path: fs.PathLike, permissionMode: fs.Mode = FOLDER_PERMISSIONS) {
if (!fs.existsSync(path)) {
fs.mkdirSync(path, permissionMode);
}
}
const TRILIUM_DATA_DIR = getTriliumDataDir(DIR_NAME);
const dataDirs = getDataDirs(TRILIUM_DATA_DIR);
export default dataDirs;

View File

@ -8,7 +8,7 @@ import protectedSessionService from "./protected_session.js";
import searchService from "../services/search/services/search.js"; import searchService from "../services/search/services/search.js";
import SearchContext from "../services/search/search_context.js"; import SearchContext from "../services/search/search_context.js";
import hoistedNoteService from "./hoisted_note.js"; import hoistedNoteService from "./hoisted_note.js";
import BNote from "../becca/entities/bnote.js"; import type BNote from "../becca/entities/bnote.js";
import { t } from "i18next"; import { t } from "i18next";
const CALENDAR_ROOT_LABEL = "calendarRoot"; const CALENDAR_ROOT_LABEL = "calendarRoot";

View File

@ -2,8 +2,8 @@
import { getContentDisposition, stripTags } from "../utils.js"; import { getContentDisposition, stripTags } from "../utils.js";
import becca from "../../becca/becca.js"; import becca from "../../becca/becca.js";
import TaskContext from "../task_context.js"; import type TaskContext from "../task_context.js";
import BBranch from "../../becca/entities/bbranch.js"; import type BBranch from "../../becca/entities/bbranch.js";
import type { Response } from "express"; import type { Response } from "express";
function exportToOpml(taskContext: TaskContext, branch: BBranch, version: string, res: Response) { function exportToOpml(taskContext: TaskContext, branch: BBranch, version: string, res: Response) {

View File

@ -5,8 +5,8 @@ import html from "html";
import { getContentDisposition, escapeHtml } from "../utils.js"; import { getContentDisposition, escapeHtml } from "../utils.js";
import mdService from "./md.js"; import mdService from "./md.js";
import becca from "../../becca/becca.js"; import becca from "../../becca/becca.js";
import TaskContext from "../task_context.js"; import type TaskContext from "../task_context.js";
import BBranch from "../../becca/entities/bbranch.js"; import type BBranch from "../../becca/entities/bbranch.js";
import type { Response } from "express"; import type { Response } from "express";
function exportSingleNote(taskContext: TaskContext, branch: BBranch, format: "html" | "markdown", res: Response) { function exportSingleNote(taskContext: TaskContext, branch: BBranch, format: "html" | "markdown", res: Response) {

View File

@ -18,7 +18,7 @@ import ValidationError from "../../errors/validation_error.js";
import type NoteMeta from "../meta/note_meta.js"; import type NoteMeta from "../meta/note_meta.js";
import type AttachmentMeta from "../meta/attachment_meta.js"; import type AttachmentMeta from "../meta/attachment_meta.js";
import type AttributeMeta from "../meta/attribute_meta.js"; import type AttributeMeta from "../meta/attribute_meta.js";
import BBranch from "../../becca/entities/bbranch.js"; import type BBranch from "../../becca/entities/bbranch.js";
import type { Response } from "express"; import type { Response } from "express";
import { RESOURCE_DIR } from "../resource_dir.js"; import { RESOURCE_DIR } from "../resource_dir.js";

View File

@ -6,8 +6,8 @@ import becca from "../becca/becca.js";
import BAttribute from "../becca/entities/battribute.js"; import BAttribute from "../becca/entities/battribute.js";
import hiddenSubtreeService from "./hidden_subtree.js"; import hiddenSubtreeService from "./hidden_subtree.js";
import oneTimeTimer from "./one_time_timer.js"; import oneTimeTimer from "./one_time_timer.js";
import BNote from "../becca/entities/bnote.js"; import type BNote from "../becca/entities/bnote.js";
import AbstractBeccaEntity from "../becca/entities/abstract_becca_entity.js"; import type AbstractBeccaEntity from "../becca/entities/abstract_becca_entity.js";
import type { DefinitionObject } from "./promoted_attribute_definition_interface.js"; import type { DefinitionObject } from "./promoted_attribute_definition_interface.js";
type Handler = (definition: DefinitionObject, note: BNote, targetNote: BNote) => void; type Handler = (definition: DefinitionObject, note: BNote, targetNote: BNote) => void;

View File

@ -9,8 +9,8 @@ import imageService from "../image.js";
import protectedSessionService from "../protected_session.js"; import protectedSessionService from "../protected_session.js";
import htmlSanitizer from "../html_sanitizer.js"; import htmlSanitizer from "../html_sanitizer.js";
import sanitizeAttributeName from "../sanitize_attribute_name.js"; import sanitizeAttributeName from "../sanitize_attribute_name.js";
import TaskContext from "../task_context.js"; import type TaskContext from "../task_context.js";
import BNote from "../../becca/entities/bnote.js"; import type BNote from "../../becca/entities/bnote.js";
import type { File } from "./common.js"; import type { File } from "./common.js";
import type { AttributeType } from "../../becca/entities/rows.js"; import type { AttributeType } from "../../becca/entities/rows.js";

View File

@ -4,8 +4,8 @@ import noteService from "../../services/notes.js";
import xml2js from "xml2js"; import xml2js from "xml2js";
import protectedSessionService from "../protected_session.js"; import protectedSessionService from "../protected_session.js";
import htmlSanitizer from "../html_sanitizer.js"; import htmlSanitizer from "../html_sanitizer.js";
import TaskContext from "../task_context.js"; import type TaskContext from "../task_context.js";
import BNote from "../../becca/entities/bnote.js"; import type BNote from "../../becca/entities/bnote.js";
const parseString = xml2js.parseString; const parseString = xml2js.parseString;
interface OpmlXml { interface OpmlXml {

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
import BNote from "../../becca/entities/bnote.js"; import type BNote from "../../becca/entities/bnote.js";
import TaskContext from "../task_context.js"; import type TaskContext from "../task_context.js";
import noteService from "../../services/notes.js"; import noteService from "../../services/notes.js";
import imageService from "../../services/image.js"; import imageService from "../../services/image.js";

View File

@ -15,11 +15,11 @@ import htmlSanitizer from "../html_sanitizer.js";
import becca from "../../becca/becca.js"; import becca from "../../becca/becca.js";
import BAttachment from "../../becca/entities/battachment.js"; import BAttachment from "../../becca/entities/battachment.js";
import markdownService from "./markdown.js"; import markdownService from "./markdown.js";
import TaskContext from "../task_context.js"; import type TaskContext from "../task_context.js";
import BNote from "../../becca/entities/bnote.js"; import type BNote from "../../becca/entities/bnote.js";
import type NoteMeta from "../meta/note_meta.js"; import type NoteMeta from "../meta/note_meta.js";
import type AttributeMeta from "../meta/attribute_meta.js"; import type AttributeMeta from "../meta/attribute_meta.js";
import { Stream } from "stream"; import type { Stream } from "stream";
import { ALLOWED_NOTE_TYPES, type NoteType } from "../../becca/entities/rows.js"; import { ALLOWED_NOTE_TYPES, type NoteType } from "../../becca/entities/rows.js";
interface MetaFile { interface MetaFile {

Some files were not shown because too many files have changed in this diff Show More