Merge remote-tracking branch 'origin/main' into feature/table_view

This commit is contained in:
Elian Doran
2025-06-27 17:08:23 +03:00
35 changed files with 1390 additions and 1229 deletions

View File

@@ -3,10 +3,10 @@
<h2>Steps</h2>
<ul>
<li>SSH into your server</li>
<li>use <code>wget</code> (or <code>curl</code>) to download latest <code>TriliumNextNotes-Server-[VERSION]-linux-x64.tar.xz</code> (copy
<li>use <code>wget</code> (or <code>curl</code>) to download latest <code>TriliumNotes-Server-[VERSION]-linux-x64.tar.xz</code> (copy
link from <a href="https://github.com/TriliumNext/Notes/releases">release page</a>,
notice <code>-Server</code> suffix) on your server.</li>
<li>unpack the archive, e.g. using <code>tar -xf -d TriliumNextNotes-Server-[VERSION]-linux-x64.tar.xz</code>
<li>unpack the archive, e.g. using <code>tar -xf -d TriliumNotes-Server-[VERSION]-linux-x64.tar.xz</code>
</li>
<li><code>cd trilium-linux-x64-server</code>
</li>
@@ -27,7 +27,7 @@
<h2>Configure Trilium to auto-run on boot with systemd</h2>
<ul>
<li>After downloading, extract and move Trilium:</li>
</ul><pre><code class="language-text-x-trilium-auto">tar -xvf TriliumNextNotes-Server-[VERSION]-linux-x64.tar.xz
</ul><pre><code class="language-text-x-trilium-auto">tar -xvf TriliumNotes-Server-[VERSION]-linux-x64.tar.xz
sudo mv trilium-linux-x64-server /opt/trilium</code></pre>
<ul>
<li>Create the service:</li>

View File

@@ -0,0 +1,22 @@
import BRevision from "./brevision.js";
describe("Revision", () => {
it("handles note with empty title properly", () => {
const revision = new BRevision({
revisionId: "4omM5OvlLhOw",
noteId: "WHMg7iFCRG3Z",
type: "text",
mime: "text/html",
isProtected: false,
title: "",
blobId: "",
dateLastEdited: "2025-06-27 14:10:39.688+0300",
dateCreated: "2025-06-27 14:10:39.688+0300",
utcDateLastEdited: "2025-06-27 14:10:39.688+0300",
utcDateCreated: "2025-06-27 14:10:39.688+0300",
utcDateModified: "2025-06-27 14:10:39.688+0300"
});
const pojo = revision.getPojo();
expect(pojo.title).toBeDefined();
});
});

View File

@@ -192,7 +192,7 @@ class BRevision extends AbstractBeccaEntity<BRevision> {
type: this.type,
mime: this.mime,
isProtected: this.isProtected,
title: this.title || undefined,
title: this.title,
blobId: this.blobId,
dateLastEdited: this.dateLastEdited,
dateCreated: this.dateCreated,
@@ -211,10 +211,10 @@ class BRevision extends AbstractBeccaEntity<BRevision> {
if (pojo.isProtected) {
if (protectedSessionService.isProtectedSessionAvailable()) {
pojo.title = protectedSessionService.encrypt(this.title) || undefined;
pojo.title = protectedSessionService.encrypt(this.title) ?? "";
} else {
// updating protected note outside of protected session means we will keep original ciphertexts
delete pojo.title;
pojo.title = "";
}
}

View File

@@ -36,6 +36,17 @@ import type Becca from "../becca/becca-interface.js";
import type { NoteParams } from "./note-interface.js";
import type { ApiParams } from "./backend_script_api_interface.js";
// Dayjs plugins
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
import isBetween from "dayjs/plugin/isBetween";
import advancedFormat from "dayjs/plugin/advancedFormat.js";
dayjs.extend(isSameOrBefore);
dayjs.extend(isSameOrAfter);
dayjs.extend(isBetween);
dayjs.extend(advancedFormat);
/**
* A whole number
* @typedef {number} int

View File

@@ -0,0 +1,114 @@
import becca from "../becca/becca.js";
import { note, NoteBuilder } from "../test/becca_mocking.js";
import cls from "./cls.js";
import { executeBundle, getScriptBundle } from "./script.js";
import BBranch from "../becca/entities/bbranch.js";
import BNote from "../becca/entities/bnote.js";
describe("Script", () => {
let rootNote!: NoteBuilder;
beforeEach(() => {
becca.reset();
rootNote = new NoteBuilder(
new BNote({
noteId: "root",
title: "root",
type: "text"
})
);
new BBranch({
branchId: "none_root",
noteId: "root",
parentNoteId: "none",
notePosition: 10
});
vi.mock("./sql.js", () => {
return {
default: {
transactional: (cb: Function) => {
cb();
},
execute: () => {},
replace: () => {},
getMap: () => {}
}
};
});
vi.mock("./sql_init.js", () => {
return {
dbReady: () => {
console.log("Hello world");
}
};
});
});
it("returns result from script", () => {
cls.init(() => {
const result = executeBundle({
script: `return "world";`,
html: "",
});
expect(result).toBe("world");
});
});
describe("dayjs", () => {
const scriptNote = note("dayjs", {
type: "code",
mime: "application/javascript;env=backend",
});
it("dayjs is available", () => {
cls.init(() => {
const bundle = getScriptBundle(scriptNote.note, true, "backend", [], `return api.dayjs().format("YYYY-MM-DD");`);
expect(bundle).toBeDefined();
const result = executeBundle(bundle!);
expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/);
});
});
it("dayjs is-same-or-before", () => {
cls.init(() => {
const bundle = getScriptBundle(scriptNote.note, true, "backend", [], `return api.dayjs("2023-10-01").isSameOrBefore(api.dayjs("2023-10-02"));`);
expect(bundle).toBeDefined();
const result = executeBundle(bundle!);
expect(result).toBe(true);
});
});
it("dayjs is-same-or-after", () => {
cls.init(() => {
const bundle = getScriptBundle(scriptNote.note, true, "backend", [], `return api.dayjs("2023-10-02").isSameOrAfter(api.dayjs("2023-10-01"));`);
expect(bundle).toBeDefined();
const result = executeBundle(bundle!);
expect(result).toBe(true);
});
});
it("dayjs is-between", () => {
cls.init(() => {
const bundle = getScriptBundle(scriptNote.note, true, "backend", [], `return api.dayjs("2023-10-02").isBetween(api.dayjs("2023-10-01"), api.dayjs("2023-10-03"));`);
expect(bundle).toBeDefined();
const result = executeBundle(bundle!);
expect(result).toBe(true);
});
});
// advanced format
it("dayjs advanced format", () => {
cls.init(() => {
const bundle = getScriptBundle(scriptNote.note, true, "backend", [], `return api.dayjs("2023-10-01").format("Q");`);
expect(bundle).toBeDefined();
const result = executeBundle(bundle!);
expect(result).not.toBe("Q");
});
});
});
});

View File

@@ -39,7 +39,7 @@ function executeNoteNoException(note: BNote, apiParams: ApiParams) {
}
}
function executeBundle(bundle: Bundle, apiParams: ApiParams = {}) {
export function executeBundle(bundle: Bundle, apiParams: ApiParams = {}) {
if (!apiParams.startNote) {
// this is the default case, the only exception is when we want to preserve frontend startNote
apiParams.startNote = bundle.note;
@@ -140,7 +140,7 @@ function getScriptBundleForFrontend(note: BNote, script?: string, params?: Scrip
return bundle;
}
function getScriptBundle(note: BNote, root: boolean = true, scriptEnv: string | null = null, includedNoteIds: string[] = [], overrideContent: string | null = null): Bundle | undefined {
export function getScriptBundle(note: BNote, root: boolean = true, scriptEnv: string | null = null, includedNoteIds: string[] = [], overrideContent: string | null = null): Bundle | undefined {
if (!note.isContentAvailable()) {
return;
}