Merge pull request #242 from TriliumNext/feature/server_esm_part2

Server ESM port: Convert some of the asynchronous imports
This commit is contained in:
Elian Doran 2024-07-23 19:17:20 +03:00 committed by GitHub
commit 0606fe01f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 196 additions and 158 deletions

View File

@ -1,6 +1,8 @@
"use strict"; "use strict";
import electron from "electron"; import electron from "electron";
import electronDebug from "electron-debug";
import electronDl from "electron-dl";
import sqlInit from "./src/services/sql_init.js"; import sqlInit from "./src/services/sql_init.js";
import appIconService from "./src/services/app_icon.js"; import appIconService from "./src/services/app_icon.js";
import windowService from "./src/services/window.js"; import windowService from "./src/services/window.js";
@ -12,11 +14,11 @@ if (require('electron-squirrel-startup')) {
} }
// Adds debug features like hotkeys for triggering dev tools and reload // Adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')(); electronDebug();
appIconService.installLocalAppIcon(); appIconService.installLocalAppIcon();
require('electron-dl')({ saveAs: true }); electronDl({ saveAs: true });
// needed for excalidraw export https://github.com/zadam/trilium/issues/4271 // needed for excalidraw export https://github.com/zadam/trilium/issues/4271
electron.app.commandLine.appendSwitch( electron.app.commandLine.appendSwitch(

11
package-lock.json generated
View File

@ -111,6 +111,7 @@
"@types/sax": "^1.2.7", "@types/sax": "^1.2.7",
"@types/semver": "^7.5.8", "@types/semver": "^7.5.8",
"@types/serve-favicon": "^2.5.7", "@types/serve-favicon": "^2.5.7",
"@types/session-file-store": "^1.2.5",
"@types/stream-throttle": "^0.1.4", "@types/stream-throttle": "^0.1.4",
"@types/tmp": "^0.2.6", "@types/tmp": "^0.2.6",
"@types/turndown": "^5.0.4", "@types/turndown": "^5.0.4",
@ -2614,6 +2615,16 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/session-file-store": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@types/session-file-store/-/session-file-store-1.2.5.tgz",
"integrity": "sha512-xjIyh40IznXLrvbAY/nmxu5cMcPcE3ZoDrSDvd02m6p8UjUgOtZAGI7Os5DDd6THuxClLWNhFo/awy1tYp64Bg==",
"dev": true,
"dependencies": {
"@types/express": "*",
"@types/express-session": "*"
}
},
"node_modules/@types/stream-throttle": { "node_modules/@types/stream-throttle": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/@types/stream-throttle/-/stream-throttle-0.1.4.tgz", "resolved": "https://registry.npmjs.org/@types/stream-throttle/-/stream-throttle-0.1.4.tgz",

View File

@ -144,6 +144,7 @@
"@types/sax": "^1.2.7", "@types/sax": "^1.2.7",
"@types/semver": "^7.5.8", "@types/semver": "^7.5.8",
"@types/serve-favicon": "^2.5.7", "@types/serve-favicon": "^2.5.7",
"@types/session-file-store": "^1.2.5",
"@types/stream-throttle": "^0.1.4", "@types/stream-throttle": "^0.1.4",
"@types/tmp": "^0.2.6", "@types/tmp": "^0.2.6",
"@types/turndown": "^5.0.4", "@types/turndown": "^5.0.4",

View File

@ -1,12 +1,12 @@
const handleParens = require('../../src/services/search/services/handle_parens'); import handleParens from "../../src/services/search/services/handle_parens";
import { TokenStructure } from "../../src/services/search/services/types";
describe("Parens handler", () => { describe("Parens handler", () => {
it("handles parens", () => { it("handles parens", () => {
const input = ["(", "hello", ")", "and", "(", "(", "pick", "one", ")", "and", "another", ")"] const input = ["(", "hello", ")", "and", "(", "(", "pick", "one", ")", "and", "another", ")"]
.map(token => ({token})); .map(token => ({token}));
expect(handleParens(input)) const actual: TokenStructure = [
.toEqual([
[ [
{token: "hello"} {token: "hello"}
], ],
@ -19,6 +19,8 @@ describe("Parens handler", () => {
{token: "and"}, {token: "and"},
{token: "another"} {token: "another"}
] ]
]); ];
expect(handleParens(input)).toEqual(actual);
}); });
}); });

View File

@ -155,9 +155,7 @@ export default class Becca {
} }
getRevision(revisionId: string): BRevision | null { getRevision(revisionId: string): BRevision | null {
const row = sql.getRow("SELECT * FROM revisions WHERE revisionId = ?", [revisionId]); const row = sql.getRow<RevisionRow | null>("SELECT * FROM revisions WHERE revisionId = ?", [revisionId]);
const BRevision = require('./entities/brevision'); // avoiding circular dependency problems
return row ? new BRevision(row) : null; return row ? new BRevision(row) : null;
} }
@ -179,9 +177,7 @@ export default class Becca {
WHERE attachmentId = ? AND isDeleted = 0` WHERE attachmentId = ? AND isDeleted = 0`
: `SELECT * FROM attachments WHERE attachmentId = ? AND isDeleted = 0`; : `SELECT * FROM attachments WHERE attachmentId = ? AND isDeleted = 0`;
const BAttachment = require('./entities/battachment'); // avoiding circular dependency problems return sql.getRows<AttachmentRow>(query, [attachmentId])
return sql.getRows(query, [attachmentId])
.map(row => new BAttachment(row))[0]; .map(row => new BAttachment(row))[0];
} }
@ -194,7 +190,6 @@ export default class Becca {
} }
getAttachments(attachmentIds: string[]): BAttachment[] { getAttachments(attachmentIds: string[]): BAttachment[] {
const BAttachment = require('./entities/battachment'); // avoiding circular dependency problems
return sql.getManyRows<AttachmentRow>("SELECT * FROM attachments WHERE attachmentId IN (???) AND isDeleted = 0", attachmentIds) return sql.getManyRows<AttachmentRow>("SELECT * FROM attachments WHERE attachmentId IN (???) AND isDeleted = 0", attachmentIds)
.map(row => new BAttachment(row)); .map(row => new BAttachment(row));
} }
@ -204,9 +199,7 @@ export default class Becca {
return null; return null;
} }
const row = sql.getRow("SELECT *, LENGTH(content) AS contentLength FROM blobs WHERE blobId = ?", [entity.blobId]); const row = sql.getRow<BBlob | null>("SELECT *, LENGTH(content) AS contentLength FROM blobs WHERE blobId = ?", [entity.blobId]);
const BBlob = require('./entities/bblob'); // avoiding circular dependency problems
return row ? new BBlob(row) : null; return row ? new BBlob(row) : null;
} }
@ -248,16 +241,12 @@ export default class Becca {
} }
getRecentNotesFromQuery(query: string, params: string[] = []): BRecentNote[] { getRecentNotesFromQuery(query: string, params: string[] = []): BRecentNote[] {
const rows = sql.getRows(query, params); const rows = sql.getRows<BRecentNote>(query, params);
const BRecentNote = require('./entities/brecent_note'); // avoiding circular dependency problems
return rows.map(row => new BRecentNote(row)); return rows.map(row => new BRecentNote(row));
} }
getRevisionsFromQuery(query: string, params: string[] = []): BRevision[] { getRevisionsFromQuery(query: string, params: string[] = []): BRevision[] {
const rows = sql.getRows<RevisionRow>(query, params); const rows = sql.getRows<RevisionRow>(query, params);
const BRevision = require('./entities/brevision'); // avoiding circular dependency problems
return rows.map(row => new BRevision(row)); return rows.map(row => new BRevision(row));
} }

View File

@ -14,13 +14,15 @@ import cls from "../services/cls.js";
import entityConstructor from "../becca/entity_constructor.js"; import entityConstructor from "../becca/entity_constructor.js";
import { AttributeRow, BranchRow, EtapiTokenRow, NoteRow, OptionRow } from './entities/rows'; import { AttributeRow, BranchRow, EtapiTokenRow, NoteRow, OptionRow } from './entities/rows';
import AbstractBeccaEntity from "./entities/abstract_becca_entity.js"; import AbstractBeccaEntity from "./entities/abstract_becca_entity.js";
import options_init from "../services/options_init.js";
import ws from "../services/ws.js";
const beccaLoaded = new Promise<void>((res, rej) => { const beccaLoaded = new Promise<void>((res, rej) => {
sqlInit.dbReady.then(() => { sqlInit.dbReady.then(() => {
cls.init(() => { cls.init(() => {
load(); load();
require('../services/options_init').initStartupOptions(); options_init.initStartupOptions();
res(); res();
}); });
@ -73,7 +75,7 @@ function load() {
function reload(reason: string) { function reload(reason: string) {
load(); load();
require('../services/ws').reloadFrontend(reason || "becca reloaded"); ws.reloadFrontend(reason || "becca reloaded");
} }
eventService.subscribeBeccaLoader([eventService.ENTITY_CHANGE_SYNCED], ({ entityName, entityRow }) => { eventService.subscribeBeccaLoader([eventService.ENTITY_CHANGE_SYNCED], ({ entityName, entityRow }) => {

View File

@ -9,6 +9,7 @@ import log from "../../services/log.js";
import { AttachmentRow } from './rows'; import { AttachmentRow } from './rows';
import BNote from "./bnote.js"; import BNote from "./bnote.js";
import BBranch from "./bbranch.js"; import BBranch from "./bbranch.js";
import noteService from "../../services/notes.js";
const attachmentRoleToNoteTypeMapping = { const attachmentRoleToNoteTypeMapping = {
'image': 'image', 'image': 'image',
@ -157,8 +158,6 @@ class BAttachment extends AbstractBeccaEntity<BAttachment> {
throw new Error(`Cannot convert protected attachment outside of protected session`); throw new Error(`Cannot convert protected attachment outside of protected session`);
} }
const noteService = require('../../services/notes');
const { note, branch } = noteService.createNewNote({ const { note, branch } = noteService.createNewNote({
parentNoteId: this.ownerId, parentNoteId: this.ownerId,
title: this.title, title: this.title,

View File

@ -8,6 +8,7 @@ import TaskContext from "../../services/task_context.js";
import cls from "../../services/cls.js"; import cls from "../../services/cls.js";
import log from "../../services/log.js"; import log from "../../services/log.js";
import { BranchRow } from './rows'; import { BranchRow } from './rows';
import handlers from "../../services/handlers.js";
/** /**
* Branch represents a relationship between a child note and its parent note. Trilium allows a note to have multiple * Branch represents a relationship between a child note and its parent note. Trilium allows a note to have multiple
@ -157,7 +158,6 @@ class BBranch extends AbstractBeccaEntity<BBranch> {
if (parentBranches.length === 1 && parentBranches[0] === this) { if (parentBranches.length === 1 && parentBranches[0] === this) {
// needs to be run before branches and attributes are deleted and thus attached relations disappear // needs to be run before branches and attributes are deleted and thus attached relations disappear
const handlers = require('../../services/handlers');
handlers.runAttachedRelations(note, 'runOnNoteDeletion', note); handlers.runAttachedRelations(note, 'runOnNoteDeletion', note);
} }
} }

View File

@ -12,10 +12,14 @@ import TaskContext from "../../services/task_context.js";
import dayjs from "dayjs"; import dayjs from "dayjs";
import utc from "dayjs/plugin/utc"; import utc from "dayjs/plugin/utc";
import eventService from "../../services/events.js"; import eventService from "../../services/events.js";
import { AttachmentRow, NoteRow, NoteType, RevisionRow } from './rows'; import { AttachmentRow, AttributeType, NoteRow, NoteType, RevisionRow } from './rows';
import BBranch from "./bbranch.js"; import BBranch from "./bbranch.js";
import BAttribute from "./battribute.js"; import BAttribute from "./battribute.js";
import { NotePojo } from '../becca-interface'; import { NotePojo } from '../becca-interface';
import searchService from "../../services/search/services/search.js";
import cloningService, { CloneResponse } from "../../services/cloning.js";
import noteService from "../../services/notes.js";
import handlers from "../../services/handlers.js";
dayjs.extend(utc); dayjs.extend(utc);
const LABEL = 'label'; const LABEL = 'label';
@ -890,11 +894,9 @@ class BNote extends AbstractBeccaEntity<BNote> {
} }
try { try {
const searchService = require('../../services/search/services/search'); const result = searchService.searchFromNote(this);
const {searchResultNoteIds} = searchService.searchFromNote(this);
const becca = this.becca; const becca = this.becca;
return (searchResultNoteIds as string[]) // TODO: remove cast once search is converted return (result.searchResultNoteIds)
.map(resultNoteId => becca.notes[resultNoteId]) .map(resultNoteId => becca.notes[resultNoteId])
.filter(note => !!note); .filter(note => !!note);
} }
@ -1261,7 +1263,7 @@ class BNote extends AbstractBeccaEntity<BNote> {
* @param name - attribute name * @param name - attribute name
* @param value - attribute value (optional) * @param value - attribute value (optional)
*/ */
setAttribute(type: string, name: string, value?: string) { setAttribute(type: AttributeType, name: string, value?: string) {
const attributes = this.getOwnedAttributes(); const attributes = this.getOwnedAttributes();
const attr = attributes.find(attr => attr.type === type && attr.name === name); const attr = attributes.find(attr => attr.type === type && attr.name === name);
@ -1274,8 +1276,6 @@ class BNote extends AbstractBeccaEntity<BNote> {
} }
} }
else { else {
const BAttribute = require('./battribute');
new BAttribute({ new BAttribute({
noteId: this.noteId, noteId: this.noteId,
type: type, type: type,
@ -1310,9 +1310,7 @@ class BNote extends AbstractBeccaEntity<BNote> {
* @param name - name of the attribute, not including the leading ~/# * @param name - name of the attribute, not including the leading ~/#
* @param value - value of the attribute - text for labels, target note ID for relations; optional. * @param value - value of the attribute - text for labels, target note ID for relations; optional.
*/ */
addAttribute(type: string, name: string, value: string = "", isInheritable: boolean = false, position: number | null = null): BAttribute { addAttribute(type: AttributeType, name: string, value: string = "", isInheritable: boolean = false, position: number | null = null): BAttribute {
const BAttribute = require('./battribute');
return new BAttribute({ return new BAttribute({
noteId: this.noteId, noteId: this.noteId,
type: type, type: type,
@ -1351,7 +1349,7 @@ class BNote extends AbstractBeccaEntity<BNote> {
* @param name - attribute name * @param name - attribute name
* @param value - attribute value (optional) * @param value - attribute value (optional)
*/ */
toggleAttribute(type: string, enabled: boolean, name: string, value?: string) { toggleAttribute(type: AttributeType, enabled: boolean, name: string, value?: string) {
if (enabled) { if (enabled) {
this.setAttribute(type, name, value); this.setAttribute(type, name, value);
} }
@ -1423,8 +1421,6 @@ class BNote extends AbstractBeccaEntity<BNote> {
} }
searchNotesInSubtree(searchString: string) { searchNotesInSubtree(searchString: string) {
const searchService = require('../../services/search/services/search');
return searchService.searchNotes(searchString) as BNote[]; return searchService.searchNotes(searchString) as BNote[];
} }
@ -1432,12 +1428,16 @@ class BNote extends AbstractBeccaEntity<BNote> {
return this.searchNotesInSubtree(searchString)[0]; return this.searchNotesInSubtree(searchString)[0];
} }
cloneTo(parentNoteId: string) { cloneTo(parentNoteId: string): CloneResponse {
const cloningService = require('../../services/cloning');
const branch = this.becca.getNote(parentNoteId)?.getParentBranches()[0]; const branch = this.becca.getNote(parentNoteId)?.getParentBranches()[0];
if (!branch?.branchId) {
return {
success: false,
message: "Unable to find the branch ID to clone."
};
}
return cloningService.cloneNoteToBranch(this.noteId, branch?.branchId); return cloningService.cloneNoteToBranch(this.noteId, branch.branchId);
} }
isEligibleForConversionToAttachment(opts: ConvertOpts = { autoConversion: false }) { isEligibleForConversionToAttachment(opts: ConvertOpts = { autoConversion: false }) {
@ -1508,7 +1508,6 @@ class BNote extends AbstractBeccaEntity<BNote> {
parentNote.setContent(fixedContent); parentNote.setContent(fixedContent);
const noteService = require('../../services/notes');
noteService.asyncPostProcessContent(parentNote, fixedContent); // to mark an unused attachment for deletion noteService.asyncPostProcessContent(parentNote, fixedContent); // to mark an unused attachment for deletion
this.deleteNote(); this.deleteNote();
@ -1535,7 +1534,6 @@ class BNote extends AbstractBeccaEntity<BNote> {
} }
// needs to be run before branches and attributes are deleted and thus attached relations disappear // needs to be run before branches and attributes are deleted and thus attached relations disappear
const handlers = require('../../services/handlers');
handlers.runAttachedRelations(this, 'runOnNoteDeletion', this); handlers.runAttachedRelations(this, 'runOnNoteDeletion', this);
taskContext.noteDeletionHandlerTriggered = true; taskContext.noteDeletionHandlerTriggered = true;

View File

@ -8,6 +8,7 @@ import AbstractBeccaEntity from "./abstract_becca_entity.js";
import sql from "../../services/sql.js"; import sql from "../../services/sql.js";
import BAttachment from "./battachment.js"; import BAttachment from "./battachment.js";
import { AttachmentRow, RevisionRow } from './rows'; import { AttachmentRow, RevisionRow } from './rows';
import eraseService from "../../services/erase.js";
interface ContentOpts { interface ContentOpts {
/** will also save this BRevision entity */ /** will also save this BRevision entity */
@ -164,7 +165,9 @@ class BRevision extends AbstractBeccaEntity<BRevision> {
* Revisions are not soft-deletable, they are immediately hard-deleted (erased). * Revisions are not soft-deletable, they are immediately hard-deleted (erased).
*/ */
eraseRevision() { eraseRevision() {
require('../../services/erase.js').eraseRevisions([this.revisionId]); if (this.revisionId) {
eraseService.eraseRevisions([this.revisionId]);
}
} }
beforeSaving() { beforeSaving() {

View File

@ -42,7 +42,7 @@ export interface OptionRow {
name: string; name: string;
value: string; value: string;
isSynced: boolean; isSynced: boolean;
utcDateModified: string; utcDateModified?: string;
} }
export interface EtapiTokenRow { export interface EtapiTokenRow {
@ -69,7 +69,7 @@ export interface AttributeRow {
noteId?: string; noteId?: string;
type: AttributeType; type: AttributeType;
name: string; name: string;
position?: number; position?: number | null;
value?: string; value?: string;
isInheritable?: boolean; isInheritable?: boolean;
utcDateModified?: string; utcDateModified?: string;

View File

@ -2,12 +2,12 @@
import imageService from "../../services/image.js"; import imageService from "../../services/image.js";
import becca from "../../becca/becca.js"; import becca from "../../becca/becca.js";
const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR;
import fs from "fs"; import fs from "fs";
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import BNote from "../../becca/entities/bnote.js"; import BNote from "../../becca/entities/bnote.js";
import BRevision from "../../becca/entities/brevision.js"; import BRevision from "../../becca/entities/brevision.js";
import { AppRequest } from '../route-interface'; import { AppRequest } from '../route-interface';
import { RESOURCE_DIR } from "../../services/resource_dir.js";
function returnImageFromNote(req: Request, res: Response) { function returnImageFromNote(req: Request, res: Response) {
const image = becca.getNote(req.params.noteId); const image = becca.getNote(req.params.noteId);

View File

@ -4,19 +4,19 @@ import { Request } from "express";
import becca from "../../becca/becca.js"; import becca from "../../becca/becca.js";
import SearchContext from "../../services/search/search_context.js"; import SearchContext from "../../services/search/search_context.js";
import searchService from "../../services/search/services/search.js"; import searchService, { EMPTY_RESULT, SearchNoteResult } from "../../services/search/services/search.js";
import bulkActionService from "../../services/bulk_actions.js"; 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 SearchResult from "../../services/search/search_result.js";
function searchFromNote(req: Request) { function searchFromNote(req: Request): SearchNoteResult {
const note = becca.getNoteOrThrow(req.params.noteId); const note = becca.getNoteOrThrow(req.params.noteId);
if (!note) { if (!note) {
// this can be triggered from recent changes, and it's harmless to return an empty list rather than fail // this can be triggered from recent changes, and it's harmless to return an empty list rather than fail
return []; return EMPTY_RESULT;
} }
if (note.type !== 'search') { if (note.type !== 'search') {

View File

@ -14,6 +14,7 @@ import ws from "../../services/ws.js";
import { Request } from 'express'; import { Request } from 'express';
import { EntityChange, EntityChangeRecord } from '../../services/entity_changes_interface'; import { EntityChange, EntityChangeRecord } from '../../services/entity_changes_interface';
import ValidationError from "../../errors/validation_error.js"; import ValidationError from "../../errors/validation_error.js";
import consistencyChecksService from "../../services/consistency_checks.js";
async function testSync() { async function testSync() {
try { try {
@ -206,7 +207,7 @@ function queueSector(req: Request) {
} }
function checkEntityChanges() { function checkEntityChanges() {
require('../../services/consistency_checks').runEntityChangesChecks(); consistencyChecksService.runEntityChangesChecks();
} }
export default { export default {

View File

@ -1,7 +1,8 @@
import session from "express-session"; import session from "express-session";
import sessionFileStore from "session-file-store";
import sessionSecret from "../services/session_secret.js"; import sessionSecret from "../services/session_secret.js";
import dataDir from "../services/data_dir.js"; import dataDir from "../services/data_dir.js";
const FileStore = require('session-file-store')(session); const FileStore = sessionFileStore(session);
const sessionParser = session({ const sessionParser = session({
secret: sessionSecret, secret: sessionSecret,

View File

@ -111,7 +111,7 @@ const ACTION_HANDLERS: Record<string, ActionHandler> = {
res = branchService.moveBranchToNote(note.getParentBranches()[0], action.targetParentNoteId); res = branchService.moveBranchToNote(note.getParentBranches()[0], action.targetParentNoteId);
} }
if (!res.success) { if ("success" in res && !res.success) {
log.info(`Moving/cloning note ${note.noteId} to ${action.targetParentNoteId} failed with error ${JSON.stringify(res)}`); log.info(`Moving/cloning note ${note.noteId} to ${action.targetParentNoteId} failed with error ${JSON.stringify(res)}`);
} }
}, },

View File

@ -1,18 +1,28 @@
"use strict"; "use strict";
const sql = require('./sql'); import sql from './sql';
const eventChangesService = require('./entity_changes'); import eventChangesService from './entity_changes';
const treeService = require('./tree'); import treeService from './tree';
const BBranch = require('../becca/entities/bbranch'); import BBranch from '../becca/entities/bbranch';
const becca = require('../becca/becca'); import becca from '../becca/becca';
const log = require('./log'); import log from './log';
function cloneNoteToParentNote(noteId: string, parentNoteId: string, prefix: string | null = null) { export interface CloneResponse {
success: boolean;
message?: string;
branchId?: string;
notePath?: string;
}
function cloneNoteToParentNote(noteId: string, parentNoteId: string, prefix: string | null = null): CloneResponse {
if (!(noteId in becca.notes) || !(parentNoteId in becca.notes)) { if (!(noteId in becca.notes) || !(parentNoteId in becca.notes)) {
return { success: false, message: 'Note cannot be cloned because either the cloned note or the intended parent is deleted.' }; return { success: false, message: 'Note cannot be cloned because either the cloned note or the intended parent is deleted.' };
} }
const parentNote = becca.getNote(parentNoteId); const parentNote = becca.getNote(parentNoteId);
if (!parentNote) {
return { success: false, message: 'Note cannot be cloned because the parent note could not be found.' };
}
if (parentNote.type === 'search') { if (parentNote.type === 'search') {
return { return {
@ -31,7 +41,7 @@ function cloneNoteToParentNote(noteId: string, parentNoteId: string, prefix: str
noteId: noteId, noteId: noteId,
parentNoteId: parentNoteId, parentNoteId: parentNoteId,
prefix: prefix, prefix: prefix,
isExpanded: 0 isExpanded: false
}).save(); }).save();
log.info(`Cloned note '${noteId}' to a new parent note '${parentNoteId}' with prefix '${prefix}'`); log.info(`Cloned note '${noteId}' to a new parent note '${parentNoteId}' with prefix '${prefix}'`);
@ -43,7 +53,7 @@ function cloneNoteToParentNote(noteId: string, parentNoteId: string, prefix: str
}; };
} }
function cloneNoteToBranch(noteId: string, parentBranchId: string, prefix: string) { function cloneNoteToBranch(noteId: string, parentBranchId: string, prefix?: string) {
const parentBranch = becca.getBranch(parentBranchId); const parentBranch = becca.getBranch(parentBranchId);
if (!parentBranch) { if (!parentBranch) {
@ -67,6 +77,9 @@ function ensureNoteIsPresentInParent(noteId: string, parentNoteId: string, prefi
const parentNote = becca.getNote(parentNoteId); const parentNote = becca.getNote(parentNoteId);
if (!parentNote) {
return { branch: null, success: false, message: "Can't find parent note." };
}
if (parentNote.type === 'search') { if (parentNote.type === 'search') {
return { branch: null, success: false, message: "Can't clone into a search note" }; return { branch: null, success: false, message: "Can't clone into a search note" };
} }
@ -81,7 +94,7 @@ function ensureNoteIsPresentInParent(noteId: string, parentNoteId: string, prefi
noteId: noteId, noteId: noteId,
parentNoteId: parentNoteId, parentNoteId: parentNoteId,
prefix: prefix, prefix: prefix,
isExpanded: 0 isExpanded: false
}).save(); }).save();
log.info(`Ensured note '${noteId}' is in parent note '${parentNoteId}' with prefix '${branch.prefix}'`); log.info(`Ensured note '${noteId}' is in parent note '${parentNoteId}' with prefix '${branch.prefix}'`);
@ -90,7 +103,7 @@ function ensureNoteIsPresentInParent(noteId: string, parentNoteId: string, prefi
} }
function ensureNoteIsAbsentFromParent(noteId: string, parentNoteId: string) { function ensureNoteIsAbsentFromParent(noteId: string, parentNoteId: string) {
const branchId = sql.getValue(`SELECT branchId FROM branches WHERE noteId = ? AND parentNoteId = ? AND isDeleted = 0`, [noteId, parentNoteId]); const branchId = sql.getValue<string>(`SELECT branchId FROM branches WHERE noteId = ? AND parentNoteId = ? AND isDeleted = 0`, [noteId, parentNoteId]);
const branch = becca.getBranch(branchId); const branch = becca.getBranch(branchId);
if (branch) { if (branch) {
@ -137,13 +150,13 @@ function cloneNoteAfter(noteId: string, afterBranchId: string) {
if (!(noteId in becca.notes)) { if (!(noteId in becca.notes)) {
return { success: false, message: `Note to be cloned '${noteId}' is deleted or does not exist.` }; return { success: false, message: `Note to be cloned '${noteId}' is deleted or does not exist.` };
} else if (!(afterNote.parentNoteId in becca.notes)) { } else if (!afterNote || !(afterNote.parentNoteId in becca.notes)) {
return { success: false, message: `After note '${afterNote.parentNoteId}' is deleted or does not exist.` }; return { success: false, message: `After note '${afterNote?.parentNoteId}' is deleted or does not exist.` };
} }
const parentNote = becca.getNote(afterNote.parentNoteId); const parentNote = becca.getNote(afterNote.parentNoteId);
if (parentNote.type === 'search') { if (!parentNote || parentNote.type === 'search') {
return { return {
success: false, success: false,
message: "Can't clone into a search note" message: "Can't clone into a search note"
@ -167,7 +180,7 @@ function cloneNoteAfter(noteId: string, afterBranchId: string) {
noteId: noteId, noteId: noteId,
parentNoteId: afterNote.parentNoteId, parentNoteId: afterNote.parentNoteId,
notePosition: afterNote.notePosition + 10, notePosition: afterNote.notePosition + 10,
isExpanded: 0 isExpanded: false
}).save(); }).save();
log.info(`Cloned note '${noteId}' into parent note '${afterNote.parentNoteId}' after note '${afterNote.noteId}', branch '${afterBranchId}'`); log.info(`Cloned note '${noteId}' into parent note '${afterNote.parentNoteId}' after note '${afterNote.noteId}', branch '${afterBranchId}'`);

View File

@ -8,6 +8,7 @@ import becca from "../becca/becca.js";
import blobService from "../services/blob.js"; import blobService from "../services/blob.js";
import { EntityChange } from './entity_changes_interface'; import { EntityChange } from './entity_changes_interface';
import type { Blob } from "./blob-interface"; import type { Blob } from "./blob-interface";
import eventService from "./events.js";
let maxEntityChangeId = 0; let maxEntityChangeId = 0;
@ -57,8 +58,6 @@ function putNoteReorderingEntityChange(parentNoteId: string, componentId?: strin
instanceId instanceId
}); });
const eventService = require('./events');
eventService.emit(eventService.ENTITY_CHANGED, { eventService.emit(eventService.ENTITY_CHANGED, {
entityName: 'note_reordering', entityName: 'note_reordering',
entity: sql.getMap(`SELECT branchId, notePosition FROM branches WHERE isDeleted = 0 AND parentNoteId = ?`, [parentNoteId]) entity: sql.getMap(`SELECT branchId, notePosition FROM branches WHERE isDeleted = 0 AND parentNoteId = ?`, [parentNoteId])

View File

@ -1,4 +1,4 @@
const log = require('./log'); import log from "./log";
const NOTE_TITLE_CHANGED = "NOTE_TITLE_CHANGED"; const NOTE_TITLE_CHANGED = "NOTE_TITLE_CHANGED";
const ENTER_PROTECTED_SESSION = "ENTER_PROTECTED_SESSION"; const ENTER_PROTECTED_SESSION = "ENTER_PROTECTED_SESSION";

View File

@ -11,7 +11,6 @@ import protectedSessionService from "../protected_session.js";
import sanitize from "sanitize-filename"; import sanitize from "sanitize-filename";
import fs from "fs"; import fs from "fs";
import becca from "../../becca/becca.js"; import becca from "../../becca/becca.js";
const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR;
import archiver from "archiver"; import archiver from "archiver";
import log from "../log.js"; import log from "../log.js";
import TaskContext from "../task_context.js"; import TaskContext from "../task_context.js";
@ -21,6 +20,7 @@ import AttachmentMeta from "../meta/attachment_meta.js";
import AttributeMeta from "../meta/attribute_meta.js"; import AttributeMeta from "../meta/attribute_meta.js";
import BBranch from "../../becca/entities/bbranch.js"; import BBranch from "../../becca/entities/bbranch.js";
import { Response } from 'express'; import { Response } from 'express';
import { RESOURCE_DIR } from "../resource_dir.js";
async function exportToZip(taskContext: TaskContext, branch: BBranch, format: "html" | "markdown", res: Response | fs.WriteStream, setHeaders = true) { async function exportToZip(taskContext: TaskContext, branch: BBranch, format: "html" | "markdown", res: Response | fs.WriteStream, setHeaders = true) {
if (!['html', 'markdown'].includes(format)) { if (!['html', 'markdown'].includes(format)) {

View File

@ -156,7 +156,6 @@ function saveImageToAttachment(noteId: string, uploadBuffer: Buffer, originalNam
setTimeout(() => { setTimeout(() => {
sql.transactional(() => { sql.transactional(() => {
const note = becca.getNoteOrThrow(noteId); const note = becca.getNoteOrThrow(noteId);
const noteService = require('../services/notes');
noteService.asyncPostProcessContent(note, note.getContent()); // to mark an unused attachment for deletion noteService.asyncPostProcessContent(note, note.getContent()); // to mark an unused attachment for deletion
}); });
}, 5000); }, 5000);

View File

@ -12,6 +12,7 @@ import sanitizeAttributeName from "../sanitize_attribute_name.js";
import TaskContext from "../task_context.js"; import TaskContext from "../task_context.js";
import BNote from "../../becca/entities/bnote.js"; import BNote from "../../becca/entities/bnote.js";
import { File } from "./common"; import { File } from "./common";
import { AttributeType } from "../../becca/entities/rows.js";
/** /**
* date format is e.g. 20181121T193703Z or 2013-04-14T16:19:00.000Z (Mac evernote, see #3496) * date format is e.g. 20181121T193703Z or 2013-04-14T16:19:00.000Z (Mac evernote, see #3496)
@ -29,7 +30,7 @@ function parseDate(text: string) {
} }
interface Attribute { interface Attribute {
type: string; type: AttributeType;
name: string; name: string;
value: string; value: string;
} }

View File

@ -50,7 +50,7 @@ function checkDate(millisSinceMidnight: number) {
return millisSinceMidnight; return millisSinceMidnight;
} }
function log(str: string) { function log(str: string | Error) {
const bundleNoteId = cls.get("bundleNoteId"); const bundleNoteId = cls.get("bundleNoteId");
if (bundleNoteId) { if (bundleNoteId) {
@ -66,11 +66,11 @@ function log(str: string) {
console.log(str); console.log(str);
} }
function info(message: string) { function info(message: string | Error) {
log(message); log(message);
} }
function error(message: string) { function error(message: string | Error) {
log(`ERROR: ${message}`); log(`ERROR: ${message}`);
} }

View File

@ -26,6 +26,7 @@ import html2plaintext from "html2plaintext";
import { AttachmentRow, AttributeRow, BranchRow, NoteRow, NoteType } from '../becca/entities/rows'; import { AttachmentRow, AttributeRow, BranchRow, NoteRow, NoteType } from '../becca/entities/rows';
import TaskContext from "./task_context.js"; import TaskContext from "./task_context.js";
import { NoteParams } from './note-interface'; import { NoteParams } from './note-interface';
import imageService from "./image.js";
interface FoundLink { interface FoundLink {
name: "imageLink" | "internalLink" | "includeNoteLink" | "relationMapLink", name: "imageLink" | "internalLink" | "includeNoteLink" | "relationMapLink",
@ -466,7 +467,7 @@ async function downloadImage(noteId: string, imageUrl: string) {
const unescapedUrl = utils.unescapeHtml(imageUrl); const unescapedUrl = utils.unescapeHtml(imageUrl);
try { try {
let imageBuffer; let imageBuffer: Buffer;
if (imageUrl.toLowerCase().startsWith("file://")) { if (imageUrl.toLowerCase().startsWith("file://")) {
imageBuffer = await new Promise((res, rej) => { imageBuffer = await new Promise((res, rej) => {
@ -487,10 +488,13 @@ async function downloadImage(noteId: string, imageUrl: string) {
const parsedUrl = url.parse(unescapedUrl); const parsedUrl = url.parse(unescapedUrl);
const title = path.basename(parsedUrl.pathname || ""); const title = path.basename(parsedUrl.pathname || "");
const imageService = require('../services/image');
const attachment = imageService.saveImageToAttachment(noteId, imageBuffer, title, true, true); const attachment = imageService.saveImageToAttachment(noteId, imageBuffer, title, true, true);
if (attachment.attachmentId) {
imageUrlToAttachmentIdMapping[imageUrl] = attachment.attachmentId; imageUrlToAttachmentIdMapping[imageUrl] = attachment.attachmentId;
} else {
log.error(`Download of '${imageUrl}' due to no attachment ID.`);
}
log.info(`Download of '${imageUrl}' succeeded and was saved as image attachment '${attachment.attachmentId}' of note '${noteId}'`); log.info(`Download of '${imageUrl}' succeeded and was saved as image attachment '${attachment.attachmentId}' of note '${noteId}'`);
} }
@ -520,7 +524,6 @@ function downloadImages(noteId: string, content: string) {
const imageBase64 = url.substr(inlineImageMatch[0].length); const imageBase64 = url.substr(inlineImageMatch[0].length);
const imageBuffer = Buffer.from(imageBase64, 'base64'); const imageBuffer = Buffer.from(imageBase64, 'base64');
const imageService = require('../services/image');
const attachment = imageService.saveImageToAttachment(noteId, imageBuffer, "inline image", true, true); const attachment = imageService.saveImageToAttachment(noteId, imageBuffer, "inline image", true, true);
const encodedTitle = encodeURIComponent(attachment.title); const encodedTitle = encodeURIComponent(attachment.title);

View File

@ -1,4 +1,5 @@
import becca from "../becca/becca.js"; import becca from "../becca/becca.js";
import BOption from "../becca/entities/boption.js";
import { OptionRow } from '../becca/entities/rows'; import { OptionRow } from '../becca/entities/rows';
import sql from "./sql.js"; import sql from "./sql.js";
@ -68,10 +69,7 @@ function setOption(name: string, value: string | number | boolean) {
} }
} }
function createOption(name: string, value: string | number, isSynced: boolean) { function createOption(name: string, value: string, isSynced: boolean) {
// to avoid circular dependency, need to find a better solution
const BOption = require('../becca/entities/boption');
new BOption({ new BOption({
name: name, name: name,
value: value, value: value,

View File

@ -137,7 +137,7 @@ function exec<T>(opts: ExecOpts): Promise<T> {
}); });
} }
function getImage(imageUrl: string) { function getImage(imageUrl: string): Promise<Buffer> {
const proxyConf = syncOptions.getSyncProxy(); const proxyConf = syncOptions.getSyncProxy();
const opts: ClientOpts = { const opts: ClientOpts = {
method: 'GET', method: 'GET',
@ -149,7 +149,7 @@ function getImage(imageUrl: string) {
const proxyAgent = getProxyAgent(opts); const proxyAgent = getProxyAgent(opts);
const parsedTargetUrl = url.parse(opts.url); const parsedTargetUrl = url.parse(opts.url);
return new Promise((resolve, reject) => { return new Promise<Buffer>((resolve, reject) => {
try { try {
const request = client.request({ const request = client.request({
method: opts.method, method: opts.method,
@ -181,8 +181,7 @@ function getImage(imageUrl: string) {
}); });
request.end(undefined); request.end(undefined);
} } catch (e: any) {
catch (e: any) {
reject(generateError(opts, e.message)); reject(generateError(opts, e.message));
} }
}); });

View File

@ -2,7 +2,7 @@ import log from "./log.js";
import path from "path"; import path from "path";
import fs from "fs"; import fs from "fs";
const RESOURCE_DIR = path.resolve(__dirname, "../.."); export const RESOURCE_DIR = path.resolve(__dirname, "../..");
// where the "trilium" executable is // where the "trilium" executable is
const ELECTRON_APP_ROOT_DIR = path.resolve(RESOURCE_DIR, "../.."); const ELECTRON_APP_ROOT_DIR = path.resolve(RESOURCE_DIR, "../..");

View File

@ -1,9 +1,9 @@
import { TokenData } from "./types"; import { TokenData, TokenStructure } from "./types";
/** /**
* This will create a recursive object from a list of tokens - tokens between parenthesis are grouped in a single array * This will create a recursive object from a list of tokens - tokens between parenthesis are grouped in a single array
*/ */
function handleParens(tokens: (TokenData | TokenData[])[]) { function handleParens(tokens: TokenStructure) {
if (tokens.length === 0) { if (tokens.length === 0) {
return []; return [];
} }

View File

@ -21,7 +21,7 @@ import utils from "../../utils.js";
import TrueExp from "../expressions/true.js"; import TrueExp from "../expressions/true.js";
import IsHiddenExp from "../expressions/is_hidden.js"; import IsHiddenExp from "../expressions/is_hidden.js";
import SearchContext from "../search_context.js"; import SearchContext from "../search_context.js";
import { TokenData } from "./types"; import { TokenData, TokenStructure } from "./types";
import Expression from "../expressions/expression.js"; import Expression from "../expressions/expression.js";
function getFulltext(_tokens: TokenData[], searchContext: SearchContext) { function getFulltext(_tokens: TokenData[], searchContext: SearchContext) {
@ -448,7 +448,7 @@ function getExpression(tokens: TokenData[], searchContext: SearchContext, level
function parse({fulltextTokens, expressionTokens, searchContext}: { function parse({fulltextTokens, expressionTokens, searchContext}: {
fulltextTokens: TokenData[], fulltextTokens: TokenData[],
expressionTokens: (TokenData | TokenData[])[], expressionTokens: TokenStructure,
searchContext: SearchContext, searchContext: SearchContext,
originalQuery: string originalQuery: string
}) { }) {

View File

@ -13,11 +13,24 @@ import log from "../../log.js";
import hoistedNoteService from "../../hoisted_note.js"; import hoistedNoteService from "../../hoisted_note.js";
import BNote from "../../../becca/entities/bnote.js"; import BNote from "../../../becca/entities/bnote.js";
import BAttribute from "../../../becca/entities/battribute.js"; import BAttribute from "../../../becca/entities/battribute.js";
import { SearchParams, TokenData } from "./types"; import { SearchParams, TokenStructure } from "./types";
import Expression from "../expressions/expression.js"; import Expression from "../expressions/expression.js";
import sql from "../../sql.js"; import sql from "../../sql.js";
import scriptService from "../../script.js";
function searchFromNote(note: BNote) { export interface SearchNoteResult {
searchResultNoteIds: string[];
highlightedTokens: string[];
error: string | null;
}
export const EMPTY_RESULT: SearchNoteResult = {
searchResultNoteIds: [],
highlightedTokens: [],
error: null
};
function searchFromNote(note: BNote): SearchNoteResult {
let searchResultNoteIds; let searchResultNoteIds;
let highlightedTokens: string[]; let highlightedTokens: string[];
@ -78,7 +91,6 @@ function searchFromRelation(note: BNote, relationName: string) {
return []; return [];
} }
const scriptService = require('../../script'); // TODO: to avoid circular dependency
const result = scriptService.executeNote(scriptNote, {originEntity: note}); const result = scriptService.executeNote(scriptNote, {originEntity: note});
if (!Array.isArray(result)) { if (!Array.isArray(result)) {
@ -273,7 +285,7 @@ function parseQueryToExpression(query: string, searchContext: SearchContext) {
const {fulltextQuery, fulltextTokens, expressionTokens} = lex(query); const {fulltextQuery, fulltextTokens, expressionTokens} = lex(query);
searchContext.fulltextQuery = fulltextQuery; searchContext.fulltextQuery = fulltextQuery;
let structuredExpressionTokens: (TokenData | TokenData[])[]; let structuredExpressionTokens: TokenStructure;
try { try {
structuredExpressionTokens = handleParens(expressionTokens); structuredExpressionTokens = handleParens(expressionTokens);

View File

@ -1,3 +1,5 @@
export type TokenStructure = (TokenData | TokenStructure)[];
export interface TokenData { export interface TokenData {
token: string; token: string;
inQuotes?: boolean; inQuotes?: boolean;

View File

@ -10,6 +10,9 @@ import dataDir from "./data_dir.js";
import cls from "./cls.js"; import cls from "./cls.js";
import fs from "fs-extra"; import fs from "fs-extra";
import Database from "better-sqlite3"; import Database from "better-sqlite3";
import ws from "./ws.js";
import becca_loader from "../becca/becca_loader.js";
import entity_changes from "./entity_changes.js";
const dbConnection: DatabaseType = new Database(dataDir.DOCUMENT_PATH); const dbConnection: DatabaseType = new Database(dataDir.DOCUMENT_PATH);
dbConnection.pragma('journal_mode = WAL'); dbConnection.pragma('journal_mode = WAL');
@ -248,7 +251,7 @@ function transactional<T>(func: (statement: Statement) => T) {
const ret = (dbConnection.transaction(func) as any).deferred(); const ret = (dbConnection.transaction(func) as any).deferred();
if (!dbConnection.inTransaction) { // i.e. transaction was really committed (and not just savepoint released) if (!dbConnection.inTransaction) { // i.e. transaction was really committed (and not just savepoint released)
require('./ws').sendTransactionEntityChangesToAllClients(); ws.sendTransactionEntityChangesToAllClients();
} }
return ret; return ret;
@ -259,11 +262,11 @@ function transactional<T>(func: (statement: Statement) => T) {
if (entityChangeIds.length > 0) { if (entityChangeIds.length > 0) {
log.info("Transaction rollback dirtied the becca, forcing reload."); log.info("Transaction rollback dirtied the becca, forcing reload.");
require('../becca/becca_loader').load(); becca_loader.load();
} }
// the maxEntityChangeId has been incremented during failed transaction, need to recalculate // the maxEntityChangeId has been incremented during failed transaction, need to recalculate
require('./entity_changes').recalculateMaxEntityChangeId(); entity_changes.recalculateMaxEntityChangeId();
throw e; throw e;
} }

View File

@ -11,6 +11,10 @@ import migrationService from "./migration.js";
import cls from "./cls.js"; import cls from "./cls.js";
import config from "./config.js"; import config from "./config.js";
import { OptionRow } from '../becca/entities/rows'; import { OptionRow } from '../becca/entities/rows';
import optionsInitService from "./options_init.js";
import BNote from "../becca/entities/bnote.js";
import BBranch from "../becca/entities/bbranch.js";
import zipImportService from "./import/zip.js";
const dbReady = utils.deferred<void>(); const dbReady = utils.deferred<void>();
@ -54,7 +58,7 @@ async function createInitialDatabase() {
const schema = fs.readFileSync(`${resourceDir.DB_INIT_DIR}/schema.sql`, "utf-8"); const schema = fs.readFileSync(`${resourceDir.DB_INIT_DIR}/schema.sql`, "utf-8");
const demoFile = fs.readFileSync(`${resourceDir.DB_INIT_DIR}/demo.zip`); const demoFile = fs.readFileSync(`${resourceDir.DB_INIT_DIR}/demo.zip`);
let rootNote; let rootNote!: BNote;
sql.transactional(() => { sql.transactional(() => {
log.info("Creating database schema ..."); log.info("Creating database schema ...");
@ -63,9 +67,6 @@ async function createInitialDatabase() {
require('../becca/becca_loader').load(); require('../becca/becca_loader').load();
const BNote = require('../becca/entities/bnote');
const BBranch = require('../becca/entities/bbranch');
log.info("Creating root note ..."); log.info("Creating root note ...");
rootNote = new BNote({ rootNote = new BNote({
@ -84,8 +85,6 @@ async function createInitialDatabase() {
notePosition: 10 notePosition: 10
}).save(); }).save();
const optionsInitService = require('./options_init');
optionsInitService.initDocumentOptions(); optionsInitService.initDocumentOptions();
optionsInitService.initNotSyncedOptions(true, {}); optionsInitService.initNotSyncedOptions(true, {});
optionsInitService.initStartupOptions(); optionsInitService.initStartupOptions();
@ -96,7 +95,6 @@ async function createInitialDatabase() {
const dummyTaskContext = new TaskContext("no-progress-reporting", 'import', false); const dummyTaskContext = new TaskContext("no-progress-reporting", 'import', false);
const zipImportService = require('./import/zip');
await zipImportService.importZip(dummyTaskContext, demoFile, rootNote); await zipImportService.importZip(dummyTaskContext, demoFile, rootNote);
sql.transactional(() => { sql.transactional(() => {
@ -106,7 +104,6 @@ async function createInitialDatabase() {
const startNoteId = sql.getValue("SELECT noteId FROM branches WHERE parentNoteId = 'root' AND isDeleted = 0 ORDER BY notePosition"); const startNoteId = sql.getValue("SELECT noteId FROM branches WHERE parentNoteId = 'root' AND isDeleted = 0 ORDER BY notePosition");
const optionService = require('./options');
optionService.setOption('openNoteContexts', JSON.stringify([ optionService.setOption('openNoteContexts', JSON.stringify([
{ {
notePath: startNoteId, notePath: startNoteId,

View File

@ -19,6 +19,8 @@ import entityConstructor from "../becca/entity_constructor.js";
import becca from "../becca/becca.js"; import becca from "../becca/becca.js";
import { EntityChange, EntityChangeRecord, EntityRow } from './entity_changes_interface'; import { EntityChange, EntityChangeRecord, EntityRow } from './entity_changes_interface';
import { CookieJar, ExecOpts } from './request_interface'; import { CookieJar, ExecOpts } from './request_interface';
import setupService from "./setup.js";
import consistency_checks from "./consistency_checks.js";
let proxyToggle = true; let proxyToggle = true;
@ -107,8 +109,6 @@ async function sync() {
} }
async function login() { async function login() {
const setupService = require('./setup'); // circular dependency issue
if (!await setupService.hasSyncServerSchemaAndSeed()) { if (!await setupService.hasSyncServerSchemaAndSeed()) {
await setupService.sendSeedToSyncServer(); await setupService.sendSeedToSyncServer();
} }
@ -282,8 +282,7 @@ async function checkContentHash(syncContext: SyncContext) {
if (failedChecks.length > 0) { if (failedChecks.length > 0) {
// before re-queuing sectors, make sure the entity changes are correct // before re-queuing sectors, make sure the entity changes are correct
const consistencyChecks = require('./consistency_checks'); consistency_checks.runEntityChangesChecks();
consistencyChecks.runEntityChangesChecks();
await syncRequest(syncContext, 'POST', `/api/sync/check-entity-changes`); await syncRequest(syncContext, 'POST', `/api/sync/check-entity-changes`);
} }

View File

@ -3,7 +3,7 @@
* (like consistency checks) can use this mutex to make sure sync isn't currently running. * (like consistency checks) can use this mutex to make sure sync isn't currently running.
*/ */
const Mutex = require('async-mutex').Mutex; import { Mutex } from "async-mutex";
const instance = new Mutex(); const instance = new Mutex();
async function doExclusively<T>(func: () => T) { async function doExclusively<T>(func: () => T) {

View File

@ -7,7 +7,13 @@ import entityChangesService from "./entity_changes.js";
import becca from "../becca/becca.js"; import becca from "../becca/becca.js";
import BNote from "../becca/entities/bnote.js"; import BNote from "../becca/entities/bnote.js";
function validateParentChild(parentNoteId: string, childNoteId: string, branchId: string | null = null) { interface ValidationResponse {
branch: BBranch | null;
success: boolean;
message?: string;
}
function validateParentChild(parentNoteId: string, childNoteId: string, branchId: string | null = null): ValidationResponse {
if (['root', '_hidden', '_share', '_lbRoot', '_lbAvailableLaunchers', '_lbVisibleLaunchers'].includes(childNoteId)) { if (['root', '_hidden', '_share', '_lbRoot', '_lbAvailableLaunchers', '_lbVisibleLaunchers'].includes(childNoteId)) {
return { branch: null, success: false, message: `Cannot change this note's location.` }; return { branch: null, success: false, message: `Cannot change this note's location.` };
} }

View File

@ -1,13 +1,15 @@
"use strict"; "use strict";
import crypto from "crypto"; import crypto from "crypto";
const randtoken = require('rand-token').generator({source: 'crypto'}); import { generator } from "rand-token";
import unescape from "unescape"; import unescape from "unescape";
import escape from "escape-html"; import escape from "escape-html";
import sanitize from "sanitize-filename"; import sanitize from "sanitize-filename";
import mimeTypes from "mime-types"; import mimeTypes from "mime-types";
import path from "path"; import path from "path";
const randtoken = generator({source: 'crypto'});
function newEntityId() { function newEntityId() {
return randomString(12); return randomString(12);
} }
@ -173,7 +175,7 @@ function replaceAll(string: string, replaceWhat: string, replaceWith: string) {
return string.replace(new RegExp(quotedReplaceWhat, "g"), replaceWith); return string.replace(new RegExp(quotedReplaceWhat, "g"), replaceWith);
} }
function formatDownloadTitle(fileName: string, type: string, mime: string) { function formatDownloadTitle(fileName: string, type: string | null, mime: string) {
if (!fileName) { if (!fileName) {
fileName = "untitled"; fileName = "untitled";
} }
@ -182,7 +184,7 @@ function formatDownloadTitle(fileName: string, type: string, mime: string) {
if (type === 'text') { if (type === 'text') {
return `${fileName}.html`; return `${fileName}.html`;
} else if (['relationMap', 'canvas', 'search'].includes(type)) { } else if (type && ['relationMap', 'canvas', 'search'].includes(type)) {
return `${fileName}.json`; return `${fileName}.json`;
} else { } else {
if (!mime) { if (!mime) {

View File

@ -15,6 +15,7 @@ import log from "../services/log.js";
import SNote from "./shaca/entities/snote.js"; import SNote from "./shaca/entities/snote.js";
import SBranch from "./shaca/entities/sbranch.js"; import SBranch from "./shaca/entities/sbranch.js";
import SAttachment from "./shaca/entities/sattachment.js"; import SAttachment from "./shaca/entities/sattachment.js";
import utils from "../services/utils.js";
function getSharedSubTreeRoot(note: SNote): { note?: SNote; branch?: SBranch } { function getSharedSubTreeRoot(note: SNote): { note?: SNote; branch?: SBranch } {
if (note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) { if (note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) {
@ -249,8 +250,6 @@ function register(router: Router) {
addNoIndexHeader(note, res); addNoIndexHeader(note, res);
const utils = require('../services/utils');
const filename = utils.formatDownloadTitle(note.title, note.type, note.mime); const filename = utils.formatDownloadTitle(note.title, note.type, note.mime);
res.setHeader('Content-Disposition', utils.getContentDisposition(filename)); res.setHeader('Content-Disposition', utils.getContentDisposition(filename));
@ -317,8 +316,6 @@ function register(router: Router) {
addNoIndexHeader(attachment.note, res); addNoIndexHeader(attachment.note, res);
const utils = require('../services/utils');
const filename = utils.formatDownloadTitle(attachment.title, null, attachment.mime); const filename = utils.formatDownloadTitle(attachment.title, null, attachment.mime);
res.setHeader('Content-Disposition', utils.getContentDisposition(filename)); res.setHeader('Content-Disposition', utils.getContentDisposition(filename));

View File

@ -1,10 +1,9 @@
"use strict"; "use strict";
import AbstractShacaEntity from "./abstract_shaca_entity";
import { SAttributeRow } from "./rows"; import { SAttributeRow } from "./rows";
import SNote from "./snote.js"; import SNote from "./snote.js";
const AbstractShacaEntity = require('./abstract_shaca_entity');
class SAttribute extends AbstractShacaEntity { class SAttribute extends AbstractShacaEntity {
attributeId: string; attributeId: string;

View File

@ -27,7 +27,7 @@ class SNote extends AbstractShacaEntity {
parentBranches: SBranch[]; parentBranches: SBranch[];
parents: SNote[]; parents: SNote[];
children: SNote[]; children: SNote[];
private ownedAttributes: SAttribute[]; ownedAttributes: SAttribute[];
private __attributeCache: SAttribute[] | null; private __attributeCache: SAttribute[] | null;
private __inheritableAttributeCache: SAttribute[] | null; private __inheritableAttributeCache: SAttribute[] | null;
targetRelations: SAttribute[]; targetRelations: SAttribute[];

View File

@ -8,7 +8,7 @@ export default class Shaca {
notes!: Record<string, SNote>; notes!: Record<string, SNote>;
branches!: Record<string, SBranch>; branches!: Record<string, SBranch>;
childParentToBranch!: Record<string, SBranch>; childParentToBranch!: Record<string, SBranch>;
private attributes!: Record<string, SAttribute>; attributes!: Record<string, SAttribute>;
attachments!: Record<string, SAttachment>; attachments!: Record<string, SAttachment>;
aliasToNote!: Record<string, SNote>; aliasToNote!: Record<string, SNote>;
shareRootNote!: SNote | null; shareRootNote!: SNote | null;

View File

@ -1,23 +1,4 @@
#!/usr/bin/env node #!/usr/bin/env node
// setup basic error handling even before requiring dependencies, since those can produce errors as well
process.on('unhandledRejection', error => {
// this makes sure that stacktrace of failed promise is printed out
console.log(error);
// but also try to log it into file
require('./services/log').info(error);
});
function exit() {
console.log("Caught interrupt/termination signal. Exiting.");
process.exit(0);
}
process.on('SIGINT', exit);
process.on('SIGTERM', exit);
import app from "./app.js"; import app from "./app.js";
import sessionParser from "./routes/session_parser.js"; import sessionParser from "./routes/session_parser.js";
import fs from "fs"; import fs from "fs";
@ -32,6 +13,25 @@ import port from "./services/port.js";
import host from "./services/host.js"; import host from "./services/host.js";
import semver from "semver"; import semver from "semver";
// setup basic error handling even before requiring dependencies, since those can produce errors as well
process.on('unhandledRejection', (error: Error) => {
// this makes sure that stacktrace of failed promise is printed out
console.log(error);
// but also try to log it into file
log.info(error);
});
function exit() {
console.log("Caught interrupt/termination signal. Exiting.");
process.exit(0);
}
process.on('SIGINT', exit);
process.on('SIGTERM', exit);
if (!semver.satisfies(process.version, ">=10.5.0")) { if (!semver.satisfies(process.version, ">=10.5.0")) {
console.error("Trilium only supports node.js 10.5 and later"); console.error("Trilium only supports node.js 10.5 and later");
process.exit(1); process.exit(1);