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";
import electron from "electron";
import electronDebug from "electron-debug";
import electronDl from "electron-dl";
import sqlInit from "./src/services/sql_init.js";
import appIconService from "./src/services/app_icon.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
require('electron-debug')();
electronDebug();
appIconService.installLocalAppIcon();
require('electron-dl')({ saveAs: true });
electronDl({ saveAs: true });
// needed for excalidraw export https://github.com/zadam/trilium/issues/4271
electron.app.commandLine.appendSwitch(

11
package-lock.json generated
View File

@ -111,6 +111,7 @@
"@types/sax": "^1.2.7",
"@types/semver": "^7.5.8",
"@types/serve-favicon": "^2.5.7",
"@types/session-file-store": "^1.2.5",
"@types/stream-throttle": "^0.1.4",
"@types/tmp": "^0.2.6",
"@types/turndown": "^5.0.4",
@ -2614,6 +2615,16 @@
"@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": {
"version": "0.1.4",
"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/semver": "^7.5.8",
"@types/serve-favicon": "^2.5.7",
"@types/session-file-store": "^1.2.5",
"@types/stream-throttle": "^0.1.4",
"@types/tmp": "^0.2.6",
"@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", () => {
it("handles parens", () => {
const input = ["(", "hello", ")", "and", "(", "(", "pick", "one", ")", "and", "another", ")"]
.map(token => ({token}));
expect(handleParens(input))
.toEqual([
const actual: TokenStructure = [
[
{token: "hello"}
],
@ -19,6 +19,8 @@ describe("Parens handler", () => {
{token: "and"},
{token: "another"}
]
]);
];
expect(handleParens(input)).toEqual(actual);
});
});

View File

@ -155,9 +155,7 @@ export default class Becca {
}
getRevision(revisionId: string): BRevision | null {
const row = sql.getRow("SELECT * FROM revisions WHERE revisionId = ?", [revisionId]);
const BRevision = require('./entities/brevision'); // avoiding circular dependency problems
const row = sql.getRow<RevisionRow | null>("SELECT * FROM revisions WHERE revisionId = ?", [revisionId]);
return row ? new BRevision(row) : null;
}
@ -179,9 +177,7 @@ export default class Becca {
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(query, [attachmentId])
return sql.getRows<AttachmentRow>(query, [attachmentId])
.map(row => new BAttachment(row))[0];
}
@ -194,7 +190,6 @@ export default class Becca {
}
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)
.map(row => new BAttachment(row));
}
@ -204,9 +199,7 @@ export default class Becca {
return null;
}
const row = sql.getRow("SELECT *, LENGTH(content) AS contentLength FROM blobs WHERE blobId = ?", [entity.blobId]);
const BBlob = require('./entities/bblob'); // avoiding circular dependency problems
const row = sql.getRow<BBlob | null>("SELECT *, LENGTH(content) AS contentLength FROM blobs WHERE blobId = ?", [entity.blobId]);
return row ? new BBlob(row) : null;
}
@ -248,16 +241,12 @@ export default class Becca {
}
getRecentNotesFromQuery(query: string, params: string[] = []): BRecentNote[] {
const rows = sql.getRows(query, params);
const BRecentNote = require('./entities/brecent_note'); // avoiding circular dependency problems
const rows = sql.getRows<BRecentNote>(query, params);
return rows.map(row => new BRecentNote(row));
}
getRevisionsFromQuery(query: string, params: string[] = []): BRevision[] {
const rows = sql.getRows<RevisionRow>(query, params);
const BRevision = require('./entities/brevision'); // avoiding circular dependency problems
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 { AttributeRow, BranchRow, EtapiTokenRow, NoteRow, OptionRow } from './entities/rows';
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) => {
sqlInit.dbReady.then(() => {
cls.init(() => {
load();
require('../services/options_init').initStartupOptions();
options_init.initStartupOptions();
res();
});
@ -73,7 +75,7 @@ function load() {
function reload(reason: string) {
load();
require('../services/ws').reloadFrontend(reason || "becca reloaded");
ws.reloadFrontend(reason || "becca reloaded");
}
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 BNote from "./bnote.js";
import BBranch from "./bbranch.js";
import noteService from "../../services/notes.js";
const attachmentRoleToNoteTypeMapping = {
'image': 'image',
@ -157,8 +158,6 @@ class BAttachment extends AbstractBeccaEntity<BAttachment> {
throw new Error(`Cannot convert protected attachment outside of protected session`);
}
const noteService = require('../../services/notes');
const { note, branch } = noteService.createNewNote({
parentNoteId: this.ownerId,
title: this.title,

View File

@ -8,6 +8,7 @@ import TaskContext from "../../services/task_context.js";
import cls from "../../services/cls.js";
import log from "../../services/log.js";
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
@ -157,7 +158,6 @@ class BBranch extends AbstractBeccaEntity<BBranch> {
if (parentBranches.length === 1 && parentBranches[0] === this) {
// 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);
}
}

View File

@ -12,10 +12,14 @@ import TaskContext from "../../services/task_context.js";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
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 BAttribute from "./battribute.js";
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);
const LABEL = 'label';
@ -890,11 +894,9 @@ class BNote extends AbstractBeccaEntity<BNote> {
}
try {
const searchService = require('../../services/search/services/search');
const {searchResultNoteIds} = searchService.searchFromNote(this);
const result = searchService.searchFromNote(this);
const becca = this.becca;
return (searchResultNoteIds as string[]) // TODO: remove cast once search is converted
return (result.searchResultNoteIds)
.map(resultNoteId => becca.notes[resultNoteId])
.filter(note => !!note);
}
@ -1261,7 +1263,7 @@ class BNote extends AbstractBeccaEntity<BNote> {
* @param name - attribute name
* @param value - attribute value (optional)
*/
setAttribute(type: string, name: string, value?: string) {
setAttribute(type: AttributeType, name: string, value?: string) {
const attributes = this.getOwnedAttributes();
const attr = attributes.find(attr => attr.type === type && attr.name === name);
@ -1274,8 +1276,6 @@ class BNote extends AbstractBeccaEntity<BNote> {
}
}
else {
const BAttribute = require('./battribute');
new BAttribute({
noteId: this.noteId,
type: type,
@ -1310,9 +1310,7 @@ class BNote extends AbstractBeccaEntity<BNote> {
* @param name - name of the attribute, not including the leading ~/#
* @param value - value of the attribute - text for labels, target note ID for relations; optional.
*/
addAttribute(type: string, name: string, value: string = "", isInheritable: boolean = false, position: number | null = null): BAttribute {
const BAttribute = require('./battribute');
addAttribute(type: AttributeType, name: string, value: string = "", isInheritable: boolean = false, position: number | null = null): BAttribute {
return new BAttribute({
noteId: this.noteId,
type: type,
@ -1351,7 +1349,7 @@ class BNote extends AbstractBeccaEntity<BNote> {
* @param name - attribute name
* @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) {
this.setAttribute(type, name, value);
}
@ -1423,8 +1421,6 @@ class BNote extends AbstractBeccaEntity<BNote> {
}
searchNotesInSubtree(searchString: string) {
const searchService = require('../../services/search/services/search');
return searchService.searchNotes(searchString) as BNote[];
}
@ -1432,12 +1428,16 @@ class BNote extends AbstractBeccaEntity<BNote> {
return this.searchNotesInSubtree(searchString)[0];
}
cloneTo(parentNoteId: string) {
const cloningService = require('../../services/cloning');
cloneTo(parentNoteId: string): CloneResponse {
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 }) {
@ -1508,7 +1508,6 @@ class BNote extends AbstractBeccaEntity<BNote> {
parentNote.setContent(fixedContent);
const noteService = require('../../services/notes');
noteService.asyncPostProcessContent(parentNote, fixedContent); // to mark an unused attachment for deletion
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
const handlers = require('../../services/handlers');
handlers.runAttachedRelations(this, 'runOnNoteDeletion', this);
taskContext.noteDeletionHandlerTriggered = true;

View File

@ -8,6 +8,7 @@ import AbstractBeccaEntity from "./abstract_becca_entity.js";
import sql from "../../services/sql.js";
import BAttachment from "./battachment.js";
import { AttachmentRow, RevisionRow } from './rows';
import eraseService from "../../services/erase.js";
interface ContentOpts {
/** 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).
*/
eraseRevision() {
require('../../services/erase.js').eraseRevisions([this.revisionId]);
if (this.revisionId) {
eraseService.eraseRevisions([this.revisionId]);
}
}
beforeSaving() {

View File

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

View File

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

View File

@ -4,19 +4,19 @@ import { Request } from "express";
import becca from "../../becca/becca.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 cls from "../../services/cls.js";
import attributeFormatter from "../../services/attribute_formatter.js";
import ValidationError from "../../errors/validation_error.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);
if (!note) {
// 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') {

View File

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

View File

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

View File

@ -111,7 +111,7 @@ const ACTION_HANDLERS: Record<string, ActionHandler> = {
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)}`);
}
},

View File

@ -1,18 +1,28 @@
"use strict";
const sql = require('./sql');
const eventChangesService = require('./entity_changes');
const treeService = require('./tree');
const BBranch = require('../becca/entities/bbranch');
const becca = require('../becca/becca');
const log = require('./log');
import sql from './sql';
import eventChangesService from './entity_changes';
import treeService from './tree';
import BBranch from '../becca/entities/bbranch';
import becca from '../becca/becca';
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)) {
return { success: false, message: 'Note cannot be cloned because either the cloned note or the intended parent is deleted.' };
}
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') {
return {
@ -31,7 +41,7 @@ function cloneNoteToParentNote(noteId: string, parentNoteId: string, prefix: str
noteId: noteId,
parentNoteId: parentNoteId,
prefix: prefix,
isExpanded: 0
isExpanded: false
}).save();
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);
if (!parentBranch) {
@ -67,6 +77,9 @@ function ensureNoteIsPresentInParent(noteId: string, parentNoteId: string, prefi
const parentNote = becca.getNote(parentNoteId);
if (!parentNote) {
return { branch: null, success: false, message: "Can't find parent note." };
}
if (parentNote.type === 'search') {
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,
parentNoteId: parentNoteId,
prefix: prefix,
isExpanded: 0
isExpanded: false
}).save();
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) {
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);
if (branch) {
@ -137,13 +150,13 @@ function cloneNoteAfter(noteId: string, afterBranchId: string) {
if (!(noteId in becca.notes)) {
return { success: false, message: `Note to be cloned '${noteId}' is deleted or does not exist.` };
} else if (!(afterNote.parentNoteId in becca.notes)) {
return { success: false, message: `After note '${afterNote.parentNoteId}' is deleted or does not exist.` };
} else if (!afterNote || !(afterNote.parentNoteId in becca.notes)) {
return { success: false, message: `After note '${afterNote?.parentNoteId}' is deleted or does not exist.` };
}
const parentNote = becca.getNote(afterNote.parentNoteId);
if (parentNote.type === 'search') {
if (!parentNote || parentNote.type === 'search') {
return {
success: false,
message: "Can't clone into a search note"
@ -167,7 +180,7 @@ function cloneNoteAfter(noteId: string, afterBranchId: string) {
noteId: noteId,
parentNoteId: afterNote.parentNoteId,
notePosition: afterNote.notePosition + 10,
isExpanded: 0
isExpanded: false
}).save();
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 { EntityChange } from './entity_changes_interface';
import type { Blob } from "./blob-interface";
import eventService from "./events.js";
let maxEntityChangeId = 0;
@ -57,8 +58,6 @@ function putNoteReorderingEntityChange(parentNoteId: string, componentId?: strin
instanceId
});
const eventService = require('./events');
eventService.emit(eventService.ENTITY_CHANGED, {
entityName: 'note_reordering',
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 ENTER_PROTECTED_SESSION = "ENTER_PROTECTED_SESSION";

View File

@ -11,7 +11,6 @@ import protectedSessionService from "../protected_session.js";
import sanitize from "sanitize-filename";
import fs from "fs";
import becca from "../../becca/becca.js";
const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR;
import archiver from "archiver";
import log from "../log.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 BBranch from "../../becca/entities/bbranch.js";
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) {
if (!['html', 'markdown'].includes(format)) {

View File

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

View File

@ -12,6 +12,7 @@ import sanitizeAttributeName from "../sanitize_attribute_name.js";
import TaskContext from "../task_context.js";
import BNote from "../../becca/entities/bnote.js";
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)
@ -29,7 +30,7 @@ function parseDate(text: string) {
}
interface Attribute {
type: string;
type: AttributeType;
name: string;
value: string;
}

View File

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

View File

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

View File

@ -1,4 +1,5 @@
import becca from "../becca/becca.js";
import BOption from "../becca/entities/boption.js";
import { OptionRow } from '../becca/entities/rows';
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) {
// to avoid circular dependency, need to find a better solution
const BOption = require('../becca/entities/boption');
function createOption(name: string, value: string, isSynced: boolean) {
new BOption({
name: name,
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 opts: ClientOpts = {
method: 'GET',
@ -149,7 +149,7 @@ function getImage(imageUrl: string) {
const proxyAgent = getProxyAgent(opts);
const parsedTargetUrl = url.parse(opts.url);
return new Promise((resolve, reject) => {
return new Promise<Buffer>((resolve, reject) => {
try {
const request = client.request({
method: opts.method,
@ -181,8 +181,7 @@ function getImage(imageUrl: string) {
});
request.end(undefined);
}
catch (e: any) {
} catch (e: any) {
reject(generateError(opts, e.message));
}
});

View File

@ -2,7 +2,7 @@ import log from "./log.js";
import path from "path";
import fs from "fs";
const RESOURCE_DIR = path.resolve(__dirname, "../..");
export const RESOURCE_DIR = path.resolve(__dirname, "../..");
// where the "trilium" executable is
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
*/
function handleParens(tokens: (TokenData | TokenData[])[]) {
function handleParens(tokens: TokenStructure) {
if (tokens.length === 0) {
return [];
}

View File

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

View File

@ -13,11 +13,24 @@ import log from "../../log.js";
import hoistedNoteService from "../../hoisted_note.js";
import BNote from "../../../becca/entities/bnote.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 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 highlightedTokens: string[];
@ -78,7 +91,6 @@ function searchFromRelation(note: BNote, relationName: string) {
return [];
}
const scriptService = require('../../script'); // TODO: to avoid circular dependency
const result = scriptService.executeNote(scriptNote, {originEntity: note});
if (!Array.isArray(result)) {
@ -273,7 +285,7 @@ function parseQueryToExpression(query: string, searchContext: SearchContext) {
const {fulltextQuery, fulltextTokens, expressionTokens} = lex(query);
searchContext.fulltextQuery = fulltextQuery;
let structuredExpressionTokens: (TokenData | TokenData[])[];
let structuredExpressionTokens: TokenStructure;
try {
structuredExpressionTokens = handleParens(expressionTokens);

View File

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

View File

@ -10,6 +10,9 @@ import dataDir from "./data_dir.js";
import cls from "./cls.js";
import fs from "fs-extra";
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);
dbConnection.pragma('journal_mode = WAL');
@ -248,7 +251,7 @@ function transactional<T>(func: (statement: Statement) => T) {
const ret = (dbConnection.transaction(func) as any).deferred();
if (!dbConnection.inTransaction) { // i.e. transaction was really committed (and not just savepoint released)
require('./ws').sendTransactionEntityChangesToAllClients();
ws.sendTransactionEntityChangesToAllClients();
}
return ret;
@ -259,11 +262,11 @@ function transactional<T>(func: (statement: Statement) => T) {
if (entityChangeIds.length > 0) {
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
require('./entity_changes').recalculateMaxEntityChangeId();
entity_changes.recalculateMaxEntityChangeId();
throw e;
}

View File

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

View File

@ -19,6 +19,8 @@ import entityConstructor from "../becca/entity_constructor.js";
import becca from "../becca/becca.js";
import { EntityChange, EntityChangeRecord, EntityRow } from './entity_changes_interface';
import { CookieJar, ExecOpts } from './request_interface';
import setupService from "./setup.js";
import consistency_checks from "./consistency_checks.js";
let proxyToggle = true;
@ -107,8 +109,6 @@ async function sync() {
}
async function login() {
const setupService = require('./setup'); // circular dependency issue
if (!await setupService.hasSyncServerSchemaAndSeed()) {
await setupService.sendSeedToSyncServer();
}
@ -282,8 +282,7 @@ async function checkContentHash(syncContext: SyncContext) {
if (failedChecks.length > 0) {
// before re-queuing sectors, make sure the entity changes are correct
const consistencyChecks = require('./consistency_checks');
consistencyChecks.runEntityChangesChecks();
consistency_checks.runEntityChangesChecks();
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.
*/
const Mutex = require('async-mutex').Mutex;
import { Mutex } from "async-mutex";
const instance = new Mutex();
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 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)) {
return { branch: null, success: false, message: `Cannot change this note's location.` };
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,23 +1,4 @@
#!/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 sessionParser from "./routes/session_parser.js";
import fs from "fs";
@ -32,6 +13,25 @@ import port from "./services/port.js";
import host from "./services/host.js";
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")) {
console.error("Trilium only supports node.js 10.5 and later");
process.exit(1);