diff --git a/src/services/app_icon.ts b/src/services/app_icon.ts index 43613e4e5..ef2b5d355 100644 --- a/src/services/app_icon.ts +++ b/src/services/app_icon.ts @@ -6,7 +6,7 @@ import log from "./log.js"; import os from "os"; import fs from "fs"; import config from "./config.js"; -import utils from "./utils.js"; +import { isElectron } from "./utils.js"; const template = `[Desktop Entry] Type=Application @@ -23,7 +23,7 @@ Terminal=false * We overwrite this file during every run as it might have been updated. */ function installLocalAppIcon() { - if (!utils.isElectron() + if (!isElectron() || ["win32", "darwin"].includes(os.platform()) || (config.General && config.General.noDesktopIcon)) { return; diff --git a/src/services/auth.ts b/src/services/auth.ts index b7f183fc4..015c7eae0 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -3,7 +3,7 @@ import etapiTokenService from "./etapi_tokens.js"; import log from "./log.js"; import sqlInit from "./sql_init.js"; -import utils from "./utils.js"; +import { isElectron } from "./utils.js"; import passwordEncryptionService from "./encryption/password_encryption.js"; import config from "./config.js"; import passwordService from "./encryption/password.js"; @@ -15,7 +15,7 @@ function checkAuth(req: Request, res: Response, next: NextFunction) { if (!sqlInit.isDbInitialized()) { res.redirect("setup"); } - else if (!req.session.loggedIn && !utils.isElectron() && !noAuthentication) { + else if (!req.session.loggedIn && !isElectron() && !noAuthentication) { res.redirect("login"); } else { @@ -26,7 +26,7 @@ function checkAuth(req: Request, res: Response, next: NextFunction) { // for electron things which need network stuff // currently, we're doing that for file upload because handling form data seems to be difficult function checkApiAuthOrElectron(req: Request, res: Response, next: NextFunction) { - if (!req.session.loggedIn && !utils.isElectron() && !noAuthentication) { + if (!req.session.loggedIn && !isElectron() && !noAuthentication) { reject(req, res, "Logged in session not found"); } else { @@ -53,7 +53,7 @@ function checkAppInitialized(req: Request, res: Response, next: NextFunction) { } function checkPasswordSet(req: Request, res: Response, next: NextFunction) { - if (!utils.isElectron() && !passwordService.isPasswordSet()) { + if (!isElectron() && !passwordService.isPasswordSet()) { res.redirect("set-password"); } else { next(); @@ -61,7 +61,7 @@ function checkPasswordSet(req: Request, res: Response, next: NextFunction) { } function checkPasswordNotSet(req: Request, res: Response, next: NextFunction) { - if (!utils.isElectron() && passwordService.isPasswordSet()) { + if (!isElectron() && passwordService.isPasswordSet()) { res.redirect("login"); } else { next(); diff --git a/src/services/backend_script_api.ts b/src/services/backend_script_api.ts index 2c3e0af05..20e68faf8 100644 --- a/src/services/backend_script_api.ts +++ b/src/services/backend_script_api.ts @@ -1,7 +1,7 @@ import log from "./log.js"; import noteService from "./notes.js"; import sql from "./sql.js"; -import utils from "./utils.js"; +import { randomString, escapeHtml, unescapeHtml } from "./utils.js"; import attributeService from "./attributes.js"; import dateNoteService from "./date_notes.js"; import treeService from "./tree.js"; @@ -549,9 +549,9 @@ function BackendScriptApi(this: Api, currentNote: BNote, apiParams: ApiParams) { this.setNoteToParent = treeService.setNoteToParent; this.transactional = sql.transactional; - this.randomString = utils.randomString; - this.escapeHtml = utils.escapeHtml; - this.unescapeHtml = utils.unescapeHtml; + this.randomString = randomString; + this.escapeHtml = escapeHtml; + this.unescapeHtml = unescapeHtml; this.sql = sql; this.getAppInfo = () => appInfo; diff --git a/src/services/blob.ts b/src/services/blob.ts index 03505075a..0040da189 100644 --- a/src/services/blob.ts +++ b/src/services/blob.ts @@ -1,7 +1,7 @@ import becca from "../becca/becca.js"; import NotFoundError from "../errors/not_found_error.js"; import protectedSessionService from "./protected_session.js"; -import utils from "./utils.js"; +import { hash } from "./utils.js"; import type { Blob } from "./blob-interface.js"; function getBlobPojo(entityName: string, entityId: string, opts?: { preview: boolean }) { @@ -51,7 +51,7 @@ function processContent(content: Buffer | string | null, isProtected: boolean, i } function calculateContentHash({blobId, content}: Blob) { - return utils.hash(`${blobId}|${content.toString()}`); + return hash(`${blobId}|${content.toString()}`); } export default { diff --git a/src/services/bulk_actions.ts b/src/services/bulk_actions.ts index 64c180405..eb99f5f2b 100644 --- a/src/services/bulk_actions.ts +++ b/src/services/bulk_actions.ts @@ -2,7 +2,7 @@ import log from "./log.js"; import becca from "../becca/becca.js"; import cloningService from "./cloning.js"; import branchService from "./branches.js"; -import utils from "./utils.js"; +import { randomString } from "./utils.js"; import eraseService from "./erase.js"; import BNote from "../becca/entities/bnote.js"; @@ -32,7 +32,7 @@ const ACTION_HANDLERS: Record = { note.addRelation(action.relationName, action.targetNoteId); }, deleteNote: (action, note) => { - const deleteId = `searchbulkaction-${utils.randomString(10)}`; + const deleteId = `searchbulkaction-${randomString(10)}`; note.deleteNote(deleteId); }, diff --git a/src/services/code_block_theme.ts b/src/services/code_block_theme.ts index 603599425..92b3bb192 100644 --- a/src/services/code_block_theme.ts +++ b/src/services/code_block_theme.ts @@ -8,7 +8,7 @@ import fs from "fs"; import themeNames from "./code_block_theme_names.json" with { type: "json" } import { t } from "i18next"; import { join } from "path"; -import utils from "./utils.js"; +import { isElectron, getResourceDir } from "./utils.js"; import env from "./env.js"; /** @@ -31,7 +31,7 @@ interface ColorTheme { * @returns the supported themes, grouped. */ export function listSyntaxHighlightingThemes() { - const path = join(utils.getResourceDir(), getStylesDirectory()); + const path = join(getResourceDir(), getStylesDirectory()); const systemThemes = readThemesFromFileSystem(path); return { @@ -46,7 +46,7 @@ export function listSyntaxHighlightingThemes() { } function getStylesDirectory() { - if (utils.isElectron() && !env.isDev()) { + if (isElectron() && !env.isDev()) { return "styles"; } diff --git a/src/services/consistency_checks.ts b/src/services/consistency_checks.ts index acbca9715..fe37c7c15 100644 --- a/src/services/consistency_checks.ts +++ b/src/services/consistency_checks.ts @@ -10,7 +10,7 @@ import entityChangesService from "./entity_changes.js"; import optionsService from "./options.js"; import BBranch from "../becca/entities/bbranch.js"; import becca from "../becca/becca.js"; -import utils from "../services/utils.js"; +import { hash as getHash, hashedBlobId, randomString } from "../services/utils.js"; import eraseService from "../services/erase.js"; import sanitizeAttributeName from "./sanitize_attribute_name.js"; import noteTypesService from "../services/note_types.js"; @@ -454,7 +454,7 @@ class ConsistencyChecks { logError(`Unable to recover note ${noteId} since it's content could not be retrieved (might be protected note).`); return; } - const blobId = utils.hashedBlobId(blankContent); + const blobId = hashedBlobId(blankContent); const blobAlreadyExists = !!sql.getValue("SELECT 1 FROM blobs WHERE blobId = ?", [blobId]); if (!blobAlreadyExists) { @@ -466,7 +466,7 @@ class ConsistencyChecks { dateModified: fakeDate }); - const hash = utils.hash(utils.randomString(10)); + const hash = getHash(randomString(10)); entityChangesService.putEntityChange({ entityName: 'blobs', @@ -689,7 +689,7 @@ class ConsistencyChecks { entityChangesService.putEntityChange({ entityName, entityId, - hash: utils.randomString(10), // doesn't matter, will force sync, but that's OK + hash: randomString(10), // doesn't matter, will force sync, but that's OK isErased: false, utcDateChanged: entityRow.utcDateModified || entityRow.utcDateCreated, isSynced: entityName !== 'options' || entityRow.isSynced diff --git a/src/services/content_hash.ts b/src/services/content_hash.ts index 70a331429..41dc9282a 100644 --- a/src/services/content_hash.ts +++ b/src/services/content_hash.ts @@ -1,7 +1,7 @@ "use strict"; import sql from "./sql.js"; -import utils from "./utils.js"; +import { hash } from "./utils.js"; import log from "./log.js"; import eraseService from "./erase.js"; @@ -43,7 +43,7 @@ function getEntityHashes() { for (const entityHashMap of Object.values(hashMap)) { for (const key in entityHashMap) { - entityHashMap[key] = utils.hash(entityHashMap[key]); + entityHashMap[key] = hash(entityHashMap[key]); } } diff --git a/src/services/encryption/password.ts b/src/services/encryption/password.ts index 05397cf2a..fe6da1690 100644 --- a/src/services/encryption/password.ts +++ b/src/services/encryption/password.ts @@ -3,7 +3,7 @@ import sql from "../sql.js"; import optionService from "../options.js"; import myScryptService from "./my_scrypt.js"; -import utils from "../utils.js"; +import { randomSecureToken, toBase64 } from "../utils.js"; import passwordEncryptionService from "./password_encryption.js"; function isPasswordSet() { @@ -25,10 +25,10 @@ function changePassword(currentPassword: string, newPassword: string) { sql.transactional(() => { const decryptedDataKey = passwordEncryptionService.getDataKey(currentPassword); - optionService.setOption('passwordVerificationSalt', utils.randomSecureToken(32)); - optionService.setOption('passwordDerivedKeySalt', utils.randomSecureToken(32)); + optionService.setOption('passwordVerificationSalt', randomSecureToken(32)); + optionService.setOption('passwordDerivedKeySalt', randomSecureToken(32)); - const newPasswordVerificationKey = utils.toBase64(myScryptService.getVerificationHash(newPassword)); + const newPasswordVerificationKey = toBase64(myScryptService.getVerificationHash(newPassword)); if (decryptedDataKey) { // TODO: what should happen if the decrypted data key is null? @@ -48,16 +48,16 @@ function setPassword(password: string) { throw new Error("Password is set already. Either change it or perform 'reset password' first."); } - optionService.createOption('passwordVerificationSalt', utils.randomSecureToken(32), true); - optionService.createOption('passwordDerivedKeySalt', utils.randomSecureToken(32), true); + optionService.createOption('passwordVerificationSalt', randomSecureToken(32), true); + optionService.createOption('passwordDerivedKeySalt', randomSecureToken(32), true); - const passwordVerificationKey = utils.toBase64(myScryptService.getVerificationHash(password)); + const passwordVerificationKey = toBase64(myScryptService.getVerificationHash(password)); optionService.createOption('passwordVerificationHash', passwordVerificationKey, true); // passwordEncryptionService expects these options to already exist optionService.createOption('encryptedDataKey', '', true); - passwordEncryptionService.setDataKey(password, utils.randomSecureToken(16)); + passwordEncryptionService.setDataKey(password, randomSecureToken(16)); return { success: true diff --git a/src/services/encryption/password_encryption.ts b/src/services/encryption/password_encryption.ts index de4277d58..fd8269871 100644 --- a/src/services/encryption/password_encryption.ts +++ b/src/services/encryption/password_encryption.ts @@ -1,10 +1,10 @@ import optionService from "../options.js"; import myScryptService from "./my_scrypt.js"; -import utils from "../utils.js"; +import { toBase64 } from "../utils.js"; import dataEncryptionService from "./data_encryption.js"; function verifyPassword(password: string) { - const givenPasswordHash = utils.toBase64(myScryptService.getVerificationHash(password)); + const givenPasswordHash = toBase64(myScryptService.getVerificationHash(password)); const dbPasswordHash = optionService.getOptionOrNull('passwordVerificationHash'); diff --git a/src/services/entity_changes.ts b/src/services/entity_changes.ts index 892e2a8bc..e15eff4de 100644 --- a/src/services/entity_changes.ts +++ b/src/services/entity_changes.ts @@ -2,7 +2,7 @@ import sql from "./sql.js"; import dateUtils from "./date_utils.js"; import log from "./log.js"; import cls from "./cls.js"; -import utils from "./utils.js"; +import { randomString } from "./utils.js"; import instanceId from "./instance_id.js"; import becca from "../becca/becca.js"; import blobService from "../services/blob.js"; @@ -30,7 +30,7 @@ function putEntityChange(origEntityChange: EntityChange) { delete ec.id; if (!ec.changeId) { - ec.changeId = utils.randomString(12); + ec.changeId = randomString(12); } ec.componentId = ec.componentId || cls.getComponentId() || "NA"; // NA = not available diff --git a/src/services/etapi_tokens.ts b/src/services/etapi_tokens.ts index 7f8d1cd2f..af20af55f 100644 --- a/src/services/etapi_tokens.ts +++ b/src/services/etapi_tokens.ts @@ -1,5 +1,5 @@ import becca from "../becca/becca.js"; -import utils from "./utils.js"; +import { fromBase64, randomSecureToken } from "./utils.js"; import BEtapiToken from "../becca/entities/betapi_token.js"; import crypto from "crypto"; @@ -12,7 +12,7 @@ function getTokenHash(token: crypto.BinaryLike) { } function createToken(tokenName: string) { - const token = utils.randomSecureToken(32); + const token = randomSecureToken(32); const tokenHash = getTokenHash(token); const etapiToken = new BEtapiToken({ @@ -34,7 +34,7 @@ function parseAuthToken(auth: string | undefined) { // allow also basic auth format for systems which allow this type of authentication // expect ETAPI token in the password field, require "etapi" username // https://github.com/zadam/trilium/issues/3181 - const basicAuthStr = utils.fromBase64(auth.substring(6)).toString("utf-8"); + const basicAuthStr = fromBase64(auth.substring(6)).toString("utf-8"); const basicAuthChunks = basicAuthStr.split(":"); if (basicAuthChunks.length !== 2) { diff --git a/src/services/export/opml.ts b/src/services/export/opml.ts index c67e1378c..b4e4c7f6e 100644 --- a/src/services/export/opml.ts +++ b/src/services/export/opml.ts @@ -1,6 +1,6 @@ "use strict"; -import utils from "../utils.js"; +import { getContentDisposition, stripTags } from "../utils.js"; import becca from "../../becca/becca.js"; import TaskContext from "../task_context.js"; import BBranch from "../../becca/entities/bbranch.js"; @@ -58,7 +58,7 @@ function exportToOpml(taskContext: TaskContext, branch: BBranch, version: string const filename = `${branch.prefix ? (`${branch.prefix} - `) : ''}${note.title}.opml`; - res.setHeader('Content-Disposition', utils.getContentDisposition(filename)); + res.setHeader('Content-Disposition', getContentDisposition(filename)); res.setHeader('Content-Type', 'text/x-opml'); res.write(` @@ -83,7 +83,7 @@ function prepareText(text: string) { const newLines = text.replace(/(]*>|)/g, '\n') .replace(/ /g, ' '); // nbsp isn't in XML standard (only HTML) - const stripped = utils.stripTags(newLines); + const stripped = stripTags(newLines); const escaped = escapeXmlAttribute(stripped); diff --git a/src/services/export/single.ts b/src/services/export/single.ts index 5e68de203..7abb99fad 100644 --- a/src/services/export/single.ts +++ b/src/services/export/single.ts @@ -2,7 +2,7 @@ import mimeTypes from "mime-types"; import html from "html"; -import utils from "../utils.js"; +import { getContentDisposition, escapeHtml } from "../utils.js"; import mdService from "./md.js"; import becca from "../../becca/becca.js"; import TaskContext from "../task_context.js"; @@ -61,7 +61,7 @@ function exportSingleNote(taskContext: TaskContext, branch: BBranch, format: "ht const fileName = `${note.title}.${extension}`; - res.setHeader('Content-Disposition', utils.getContentDisposition(fileName)); + res.setHeader('Content-Disposition', getContentDisposition(fileName)); res.setHeader('Content-Type', `${mime}; charset=UTF-8`); res.send(payload); @@ -119,7 +119,7 @@ function inlineAttachments(content: string) { const base64Content = attachmentContent.toString('base64'); const hrefValue = `data:${attachment.mime};base64,${base64Content}`; - return `href="${hrefValue}" download="${utils.escapeHtml(attachment.title)}"`; + return `href="${hrefValue}" download="${escapeHtml(attachment.title)}"`; }); return content; diff --git a/src/services/export/zip.ts b/src/services/export/zip.ts index 0cf350102..1c31c2cae 100644 --- a/src/services/export/zip.ts +++ b/src/services/export/zip.ts @@ -6,7 +6,7 @@ import path from "path"; import mimeTypes from "mime-types"; import mdService from "./md.js"; import packageInfo from "../../../package.json" with { type: "json" }; -import utils from "../utils.js"; +import { getContentDisposition, escapeHtml } from "../utils.js"; import protectedSessionService from "../protected_session.js"; import sanitize from "sanitize-filename"; import fs from "fs"; @@ -311,7 +311,7 @@ async function exportToZip(taskContext: TaskContext, branch: BBranch, format: "h if (!noteMeta?.notePath?.length) { throw new Error("Missing note path."); } const cssUrl = `${"../".repeat(noteMeta.notePath.length - 1)}style.css`; - const htmlTitle = utils.escapeHtml(title); + const htmlTitle = escapeHtml(title); // element will make sure external links are openable - https://github.com/zadam/trilium/issues/1289#issuecomment-704066809 content = ` @@ -411,7 +411,7 @@ ${markdownContent}`; function saveNavigationInner(meta: NoteMeta) { let html = '
  • '; - const escapedTitle = utils.escapeHtml(`${meta.prefix ? `${meta.prefix} - ` : ''}${meta.title}`); + const escapedTitle = escapeHtml(`${meta.prefix ? `${meta.prefix} - ` : ''}${meta.title}`); if (meta.dataFileName && meta.noteId) { const targetUrl = getNoteTargetUrl(meta.noteId, rootMeta); @@ -568,7 +568,7 @@ ${markdownContent}`; const zipFileName = `${branch.prefix ? `${branch.prefix} - ` : ""}${note.getTitleOrProtected()}.zip`; if (setHeaders && "setHeader" in res) { - res.setHeader('Content-Disposition', utils.getContentDisposition(zipFileName)); + res.setHeader('Content-Disposition', getContentDisposition(zipFileName)); res.setHeader('Content-Type', 'application/zip'); } diff --git a/src/services/host.ts b/src/services/host.ts index 1324df040..fe5302bf2 100644 --- a/src/services/host.ts +++ b/src/services/host.ts @@ -1,9 +1,9 @@ import config from "./config.js"; -import utils from "./utils.js"; +import { isElectron } from "./utils.js"; function getHost() { const envHost = process.env.TRILIUM_HOST; - if (envHost && !utils.isElectron) { + if (envHost && !isElectron) { return envHost; } diff --git a/src/services/import/enex.ts b/src/services/import/enex.ts index 543523a92..071b66cdb 100644 --- a/src/services/import/enex.ts +++ b/src/services/import/enex.ts @@ -2,7 +2,7 @@ import sax from "sax"; import stream from "stream"; import { Throttle } from 'stream-throttle'; import log from "../log.js"; -import utils from "../utils.js"; +import { md5, escapeHtml, fromBase64 } from "../utils.js"; import sql from "../sql.js"; import noteService from "../notes.js"; import imageService from "../image.js"; @@ -291,10 +291,10 @@ function importEnex(taskContext: TaskContext, file: File, parentNote: BNote): Pr } if (typeof resource.content === "string") { - resource.content = utils.fromBase64(resource.content); + resource.content = fromBase64(resource.content); } - const hash = utils.md5(resource.content); + const hash = md5(resource.content); // skip all checked/unchecked checkboxes from OneNote if (['74de5d3d1286f01bac98d32a09f601d9', @@ -332,7 +332,7 @@ function importEnex(taskContext: TaskContext, file: File, parentNote: BNote): Pr taskContext.increaseProgressCount(); - const resourceLink = `${utils.escapeHtml(resource.title)}`; + const resourceLink = `${escapeHtml(resource.title)}`; content = (content || "").replace(mediaRegex, resourceLink); }; diff --git a/src/services/import/single.ts b/src/services/import/single.ts index a1308a807..3791b6ea1 100644 --- a/src/services/import/single.ts +++ b/src/services/import/single.ts @@ -8,7 +8,7 @@ import imageService from "../../services/image.js"; import protectedSessionService from "../protected_session.js"; import markdownService from "./markdown.js"; import mimeService from "./mime.js"; -import utils from "../../services/utils.js"; +import { getNoteTitle } from "../../services/utils.js"; import importUtils from "./utils.js"; import htmlSanitizer from "../html_sanitizer.js"; import { File } from "./common.js"; @@ -68,7 +68,7 @@ function importFile(taskContext: TaskContext, file: File, parentNote: BNote) { } function importCodeNote(taskContext: TaskContext, file: File, parentNote: BNote) { - const title = utils.getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); + const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); const content = file.buffer.toString("utf-8"); const detectedMime = mimeService.getMime(file.originalname) || file.mimetype; const mime = mimeService.normalizeMimeType(detectedMime); @@ -88,7 +88,7 @@ function importCodeNote(taskContext: TaskContext, file: File, parentNote: BNote) } function importPlainText(taskContext: TaskContext, file: File, parentNote: BNote) { - const title = utils.getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); + const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); const plainTextContent = file.buffer.toString("utf-8"); const htmlContent = convertTextToHtml(plainTextContent); @@ -125,7 +125,7 @@ function convertTextToHtml(text: string) { } function importMarkdown(taskContext: TaskContext, file: File, parentNote: BNote) { - const title = utils.getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); + const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); const markdownContent = file.buffer.toString("utf-8"); let htmlContent = markdownService.renderToHtml(markdownContent, title); @@ -154,7 +154,7 @@ function importHtml(taskContext: TaskContext, file: File, parentNote: BNote) { // Try to get title from HTML first, fall back to filename // We do this before sanitization since that turns all

    s into

    const htmlTitle = importUtils.extractHtmlTitle(content); - const title = htmlTitle || utils.getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); + const title = htmlTitle || getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); content = importUtils.handleH1(content, title); diff --git a/src/services/import/zip.ts b/src/services/import/zip.ts index 15d60482b..aa5a5822d 100644 --- a/src/services/import/zip.ts +++ b/src/services/import/zip.ts @@ -1,7 +1,7 @@ "use strict"; import BAttribute from "../../becca/entities/battribute.js"; -import utils from "../../services/utils.js"; +import { removeTextFileExtension, newEntityId, getNoteTitle } from "../../services/utils.js"; import log from "../../services/log.js"; import noteService from "../../services/notes.js"; import attributeService from "../../services/attributes.js"; @@ -51,7 +51,7 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo } if (!noteIdMap[origNoteId]) { - noteIdMap[origNoteId] = utils.newEntityId(); + noteIdMap[origNoteId] = newEntityId(); } return noteIdMap[origNoteId]; @@ -64,7 +64,7 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo } if (!attachmentIdMap[origAttachmentId]) { - attachmentIdMap[origAttachmentId] = utils.newEntityId(); + attachmentIdMap[origAttachmentId] = newEntityId(); } return attachmentIdMap[origAttachmentId]; @@ -152,13 +152,13 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo // in case we lack metadata, we treat e.g. "Programming.html" and "Programming" as the same note // (one data file, the other directory for children) - const filePathNoExt = utils.removeTextFileExtension(filePath); + const filePathNoExt = removeTextFileExtension(filePath); if (filePathNoExt in createdPaths) { return createdPaths[filePathNoExt]; } - const noteId = utils.newEntityId(); + const noteId = newEntityId(); createdPaths[filePathNoExt] = noteId; @@ -225,7 +225,7 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo return; } - const noteTitle = utils.getNoteTitle(filePath, !!taskContext.data?.replaceUnderscoresWithSpaces, noteMeta); + const noteTitle = getNoteTitle(filePath, !!taskContext.data?.replaceUnderscoresWithSpaces, noteMeta); const parentNoteId = getParentNoteId(filePath, parentNoteMeta); if (!parentNoteId) { @@ -466,7 +466,7 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo content = content.toString("utf-8"); } - const noteTitle = utils.getNoteTitle(filePath, taskContext.data?.replaceUnderscoresWithSpaces || false, noteMeta); + const noteTitle = getNoteTitle(filePath, taskContext.data?.replaceUnderscoresWithSpaces || false, noteMeta); content = processNoteContent(noteMeta, type, mime, content, noteTitle || "", filePath); diff --git a/src/services/instance_id.ts b/src/services/instance_id.ts index 047325e6c..31e620a8f 100644 --- a/src/services/instance_id.ts +++ b/src/services/instance_id.ts @@ -1,5 +1,5 @@ -import utils from "./utils.js"; +import { randomString } from "./utils.js"; -const instanceId = utils.randomString(12); +const instanceId = randomString(12); export default instanceId; diff --git a/src/services/keyboard_actions.ts b/src/services/keyboard_actions.ts index ecf659041..00f5764c8 100644 --- a/src/services/keyboard_actions.ts +++ b/src/services/keyboard_actions.ts @@ -2,12 +2,12 @@ import optionService from "./options.js"; import log from "./log.js"; -import utils from "./utils.js"; +import { isElectron as getIsElectron } from "./utils.js"; import { KeyboardShortcut } from './keyboard_actions_interface.js'; import { t } from "i18next"; const isMac = process.platform === "darwin"; -const isElectron = utils.isElectron(); +const isElectron = getIsElectron(); function getDefaultKeyboardActions() { if (!t("keyboard_actions.note-navigation")) { diff --git a/src/services/migration.ts b/src/services/migration.ts index 27d0308f2..b4db32410 100644 --- a/src/services/migration.ts +++ b/src/services/migration.ts @@ -2,7 +2,7 @@ import backupService from "./backup.js"; import sql from "./sql.js"; import fs from "fs-extra"; import log from "./log.js"; -import utils from "./utils.js"; +import { crash } from "./utils.js"; import resourceDir from "./resource_dir.js"; import appInfo from "./app_info.js"; import cls from "./cls.js"; @@ -20,7 +20,7 @@ async function migrate() { if (currentDbVersion < 214) { log.error("Direct migration from your current version is not supported. Please upgrade to the latest v0.60.4 first and only then to this version."); - await utils.crash(); + await crash(); return; } @@ -83,7 +83,7 @@ async function migrate() { log.error(`error during migration to version ${mig.dbVersion}: ${e.stack}`); log.error("migration failed, crashing hard"); // this is not very user-friendly :-/ - utils.crash(); + crash(); break; // crash() is sometimes async } } @@ -135,7 +135,7 @@ async function migrateIfNecessary() { if (currentDbVersion > appInfo.dbVersion && process.env.TRILIUM_IGNORE_DB_VERSION !== 'true') { log.error(`Current DB version ${currentDbVersion} is newer than the current DB version ${appInfo.dbVersion}, which means that it was created by a newer and incompatible version of Trilium. Upgrade to the latest version of Trilium to resolve this issue.`); - await utils.crash(); + await crash(); } if (!isDbUpToDate()) { diff --git a/src/services/notes.ts b/src/services/notes.ts index 73ec387fa..193dde4d6 100644 --- a/src/services/notes.ts +++ b/src/services/notes.ts @@ -6,7 +6,7 @@ import eventService from "./events.js"; import cls from "../services/cls.js"; import protectedSessionService from "../services/protected_session.js"; import log from "../services/log.js"; -import utils from "../services/utils.js"; +import { newEntityId, isString, unescapeHtml, quoteRegex, toMap } from "../services/utils.js"; import revisionService from "./revisions.js"; import request from "./request.js"; import path from "path"; @@ -465,7 +465,7 @@ function findRelationMapLinks(content: string, foundLinks: FoundLink[]) { const imageUrlToAttachmentIdMapping: Record = {}; async function downloadImage(noteId: string, imageUrl: string) { - const unescapedUrl = utils.unescapeHtml(imageUrl); + const unescapedUrl = unescapeHtml(imageUrl); try { let imageBuffer: Buffer; @@ -508,7 +508,7 @@ async function downloadImage(noteId: string, imageUrl: string) { const downloadImagePromises: Record> = {}; function replaceUrl(content: string, url: string, attachment: Attachment) { - const quotedUrl = utils.quoteRegex(url); + const quotedUrl = quoteRegex(url); return content.replace(new RegExp(`\\s+src=[\"']${quotedUrl}[\"']`, "ig"), ` src="api/attachments/${attachment.attachmentId}/image/${encodeURIComponent(attachment.title)}"`); } @@ -744,7 +744,7 @@ function updateNoteData(noteId: string, content: string, attachments: Attachment note.setContent(newContent, { forceFrontendReload }); if (attachments?.length > 0) { - const existingAttachmentsByTitle = utils.toMap(note.getAttachments({includeContentLength: false}), 'title'); + const existingAttachmentsByTitle = toMap(note.getAttachments({includeContentLength: false}), 'title'); for (const {attachmentId, role, mime, title, position, content} of attachments) { if (attachmentId || !(title in existingAttachmentsByTitle)) { @@ -886,7 +886,7 @@ async function asyncPostProcessContent(note: BNote, content: string | Buffer) { return; } - if (note.hasStringContent() && !utils.isString(content)) { + if (note.hasStringContent() && !isString(content)) { content = content.toString(); } @@ -1035,7 +1035,7 @@ function getNoteIdMapping(origNote: BNote) { // pregenerate new noteIds since we'll need to fix relation references even for not yet created notes for (const origNoteId of origNote.getDescendantNoteIds()) { - noteIdMapping[origNoteId] = utils.newEntityId(); + noteIdMapping[origNoteId] = newEntityId(); } return noteIdMapping; diff --git a/src/services/options_init.ts b/src/services/options_init.ts index a48de12e3..2c86fd933 100644 --- a/src/services/options_init.ts +++ b/src/services/options_init.ts @@ -1,15 +1,15 @@ import optionService from "./options.js"; import type { OptionMap } from "./options.js"; import appInfo from "./app_info.js"; -import utils from "./utils.js"; +import { randomSecureToken } from "./utils.js"; import log from "./log.js"; import dateUtils from "./date_utils.js"; import keyboardActions from "./keyboard_actions.js"; import { KeyboardShortcutWithRequiredActionName } from './keyboard_actions_interface.js'; function initDocumentOptions() { - optionService.createOption('documentId', utils.randomSecureToken(16), false); - optionService.createOption('documentSecret', utils.randomSecureToken(16), false); + optionService.createOption('documentId', randomSecureToken(16), false); + optionService.createOption('documentSecret', randomSecureToken(16), false); } /** diff --git a/src/services/port.ts b/src/services/port.ts index ffb2e2bf2..0fffc7c24 100644 --- a/src/services/port.ts +++ b/src/services/port.ts @@ -1,5 +1,5 @@ import config from "./config.js"; -import utils from "./utils.js"; +import { isElectron } from "./utils.js"; import env from "./env.js"; import dataDir from "./data_dir.js"; @@ -18,7 +18,7 @@ let port: number; if (process.env.TRILIUM_PORT) { port = parseAndValidate(process.env.TRILIUM_PORT, "environment variable TRILIUM_PORT"); -} else if (utils.isElectron()) { +} else if (isElectron()) { port = env.isDev() ? 37740 : 37840; } else { port = parseAndValidate(config['Network']['port'] || '3000', `Network.port in ${dataDir.CONFIG_INI_PATH}`); diff --git a/src/services/request.ts b/src/services/request.ts index 3fa70e5e7..2273c9dff 100644 --- a/src/services/request.ts +++ b/src/services/request.ts @@ -1,6 +1,6 @@ "use strict"; -import utils from "./utils.js"; +import { isElectron } from "./utils.js"; import log from "./log.js"; import url from "url"; import syncOptions from "./sync_options.js"; @@ -210,7 +210,7 @@ async function getProxyAgent(opts: ClientOpts) { async function getClient(opts: ClientOpts): Promise { // it's not clear how to explicitly configure proxy (as opposed to system proxy), // so in that case, we always use node's modules - if (utils.isElectron() && !opts.proxy) { + if (isElectron() && !opts.proxy) { return (await import('electron')).net as Client; } else { const {protocol} = url.parse(opts.url); diff --git a/src/services/script_context.ts b/src/services/script_context.ts index 313c6fcc7..cf016b492 100644 --- a/src/services/script_context.ts +++ b/src/services/script_context.ts @@ -1,4 +1,4 @@ -import utils from "./utils.js"; +import { toObject } from "./utils.js"; import BackendScriptApi from "./backend_script_api.js"; import BNote from "../becca/entities/bnote.js"; import { ApiParams } from './backend_script_api_interface.js'; @@ -16,8 +16,8 @@ class ScriptContext { constructor(allNotes: BNote[], apiParams: ApiParams) { this.allNotes = allNotes; this.modules = {}; - this.notes = utils.toObject(allNotes, note => [note.noteId, note]); - this.apis = utils.toObject(allNotes, note => [note.noteId, new BackendScriptApi(note, apiParams)]); + this.notes = toObject(allNotes, note => [note.noteId, note]); + this.apis = toObject(allNotes, note => [note.noteId, new BackendScriptApi(note, apiParams)]); } require(moduleNoteIds: string[]) { diff --git a/src/services/search/expressions/note_content_fulltext.ts b/src/services/search/expressions/note_content_fulltext.ts index 9ede1a44a..d81f42dcd 100644 --- a/src/services/search/expressions/note_content_fulltext.ts +++ b/src/services/search/expressions/note_content_fulltext.ts @@ -9,7 +9,7 @@ import log from "../../log.js"; import becca from "../../../becca/becca.js"; import protectedSessionService from "../../protected_session.js"; import striptags from "striptags"; -import utils from "../../utils.js"; +import { normalize } from "../../utils.js"; import sql from "../../sql.js"; @@ -125,7 +125,7 @@ class NoteContentFulltextExp extends Expression { } preprocessContent(content: string | Buffer, type: string, mime: string) { - content = utils.normalize(content.toString()); + content = normalize(content.toString()); if (type === 'text' && mime === 'text/html') { if (!this.raw && content.length < 20000) { // striptags is slow for very large notes @@ -183,7 +183,7 @@ class NoteContentFulltextExp extends Expression { const topicsString = topicsArray.join(", "); - content = utils.normalize(topicsString.toString()); + content = normalize(topicsString.toString()); } else if (type === 'canvas' && mime === 'application/json') { interface Element { @@ -199,7 +199,7 @@ class NoteContentFulltextExp extends Expression { .filter((element: Element) => element.type === 'text' && element.text) // Filter for 'text' type elements with a 'text' property .map((element: Element) => element.text!); // Use `!` to assert `text` is defined after filtering - content =utils.normalize(texts.toString()) + content = normalize(texts.toString()) } diff --git a/src/services/search/expressions/note_flat_text.ts b/src/services/search/expressions/note_flat_text.ts index 3bd101005..17363a91c 100644 --- a/src/services/search/expressions/note_flat_text.ts +++ b/src/services/search/expressions/note_flat_text.ts @@ -6,7 +6,7 @@ import SearchContext from "../search_context.js"; import Expression from "./expression.js"; import NoteSet from "../note_set.js"; import becca from "../../../becca/becca.js"; -import utils from "../../utils.js"; +import { normalize } from "../../utils.js"; import beccaService from "../../../becca/becca_service.js"; class NoteFlatTextExp extends Expression { @@ -61,8 +61,8 @@ class NoteFlatTextExp extends Expression { } for (const attribute of note.getOwnedAttributes()) { - const normalizedName = utils.normalize(attribute.name); - const normalizedValue = utils.normalize(attribute.value); + const normalizedName = normalize(attribute.name); + const normalizedValue = normalize(attribute.value); for (const token of remainingTokens) { if (normalizedName.includes(token) || normalizedValue.includes(token)) { @@ -72,7 +72,7 @@ class NoteFlatTextExp extends Expression { } for (const parentNote of note.parents) { - const title = utils.normalize(beccaService.getNoteTitle(note.noteId, parentNote.noteId)); + const title = normalize(beccaService.getNoteTitle(note.noteId, parentNote.noteId)); const foundTokens = foundAttrTokens.slice(); for (const token of remainingTokens) { @@ -109,8 +109,8 @@ class NoteFlatTextExp extends Expression { } for (const attribute of note.ownedAttributes) { - if (utils.normalize(attribute.name).includes(token) - || utils.normalize(attribute.value).includes(token)) { + if (normalize(attribute.name).includes(token) + || normalize(attribute.value).includes(token)) { foundAttrTokens.push(token); } @@ -118,7 +118,7 @@ class NoteFlatTextExp extends Expression { } for (const parentNote of note.parents) { - const title = utils.normalize(beccaService.getNoteTitle(note.noteId, parentNote.noteId)); + const title = normalize(beccaService.getNoteTitle(note.noteId, parentNote.noteId)); const foundTokens = foundAttrTokens.slice(); for (const token of this.tokens) { diff --git a/src/services/search/services/parse.ts b/src/services/search/services/parse.ts index 21187e112..d83f4fd39 100644 --- a/src/services/search/services/parse.ts +++ b/src/services/search/services/parse.ts @@ -17,7 +17,7 @@ import OrderByAndLimitExp from "../expressions/order_by_and_limit.js"; import AncestorExp from "../expressions/ancestor.js"; import buildComparator from "./build_comparator.js"; import ValueExtractor from "../value_extractor.js"; -import utils from "../../utils.js"; +import { removeDiacritic } from "../../utils.js"; import TrueExp from "../expressions/true.js"; import IsHiddenExp from "../expressions/is_hidden.js"; import SearchContext from "../search_context.js"; @@ -25,7 +25,7 @@ import { TokenData, TokenStructure } from "./types.js"; import Expression from "../expressions/expression.js"; function getFulltext(_tokens: TokenData[], searchContext: SearchContext) { - const tokens: string[] = _tokens.map(t => utils.removeDiacritic(t.token)); + const tokens: string[] = _tokens.map(t => removeDiacritic(t.token)); searchContext.highlightedTokens.push(...tokens); diff --git a/src/services/search/services/search.ts b/src/services/search/services/search.ts index 1b941adda..663851325 100644 --- a/src/services/search/services/search.ts +++ b/src/services/search/services/search.ts @@ -8,7 +8,7 @@ import SearchResult from "../search_result.js"; import SearchContext from "../search_context.js"; import becca from "../../../becca/becca.js"; import beccaService from "../../../becca/becca_service.js"; -import utils from "../../utils.js"; +import { normalize, escapeHtml, escapeRegExp } from "../../utils.js"; import log from "../../log.js"; import hoistedNoteService from "../../hoisted_note.js"; import BNote from "../../../becca/entities/bnote.js"; @@ -403,8 +403,8 @@ function highlightSearchResults(searchResults: SearchResult[], highlightedTokens continue; } - if (highlightedTokens.find(token => utils.normalize(attr.name).includes(token) - || utils.normalize(attr.value).includes(token))) { + if (highlightedTokens.find(token => normalize(attr.name).includes(token) + || normalize(attr.value).includes(token))) { result.highlightedNotePathTitle += ` "${formatAttribute(attr)}'`; } @@ -423,7 +423,7 @@ function highlightSearchResults(searchResults: SearchResult[], highlightedTokens for (const result of searchResults) { // Reset token - const tokenRegex = new RegExp(utils.escapeRegExp(token), "gi"); + const tokenRegex = new RegExp(escapeRegExp(token), "gi"); let match; // Find all matches @@ -449,15 +449,15 @@ function highlightSearchResults(searchResults: SearchResult[], highlightedTokens function formatAttribute(attr: BAttribute) { if (attr.type === 'relation') { - return `~${utils.escapeHtml(attr.name)}=…`; + return `~${escapeHtml(attr.name)}=…`; } else if (attr.type === 'label') { - let label = `#${utils.escapeHtml(attr.name)}`; + let label = `#${escapeHtml(attr.name)}`; if (attr.value) { const val = /[^\w-]/.test(attr.value) ? `"${attr.value}"` : attr.value; - label += `=${utils.escapeHtml(val)}`; + label += `=${escapeHtml(val)}`; } return label; diff --git a/src/services/session_secret.ts b/src/services/session_secret.ts index 36e5d53af..a536ebaaa 100644 --- a/src/services/session_secret.ts +++ b/src/services/session_secret.ts @@ -3,7 +3,7 @@ import fs from "fs"; import dataDir from "./data_dir.js"; import log from "./log.js"; -import utils from "./utils.js" +import { randomSecureToken } from "./utils.js" const sessionSecretPath = `${dataDir.TRILIUM_DATA_DIR}/session_secret.txt`; @@ -12,7 +12,7 @@ let sessionSecret: string; const ENCODING = "ascii"; if (!fs.existsSync(sessionSecretPath)) { - sessionSecret = utils.randomSecureToken(64).slice(0, 64); + sessionSecret = randomSecureToken(64).slice(0, 64); log.info("Generated session secret"); diff --git a/src/services/setup.ts b/src/services/setup.ts index 2f38cc0ac..757444709 100644 --- a/src/services/setup.ts +++ b/src/services/setup.ts @@ -5,7 +5,7 @@ import optionService from "./options.js"; import syncOptions from "./sync_options.js"; import request from "./request.js"; import appInfo from "./app_info.js"; -import utils from "./utils.js"; +import { timeLimit } from "./utils.js"; import becca from "../becca/becca.js"; import { SetupStatusResponse, SetupSyncSeedResponse } from './api-interface.js'; @@ -47,7 +47,7 @@ async function sendSeedToSyncServer() { async function requestToSyncServer(method: string, path: string, body?: string | {}): Promise { const timeout = syncOptions.getSyncTimeout(); - return await utils.timeLimit(request.exec({ + return await timeLimit(request.exec({ method, url: syncOptions.getSyncServerHost() + path, body, diff --git a/src/services/sql_init.ts b/src/services/sql_init.ts index 0bcf77002..44c4a4043 100644 --- a/src/services/sql_init.ts +++ b/src/services/sql_init.ts @@ -2,7 +2,7 @@ import log from "./log.js"; import fs from "fs"; import resourceDir from "./resource_dir.js"; import sql from "./sql.js"; -import utils from "./utils.js"; +import { isElectron, deferred } from "./utils.js"; import optionService from "./options.js"; import port from "./port.js"; import BOption from "../becca/entities/boption.js"; @@ -18,7 +18,7 @@ import becca_loader from "../becca/becca_loader.js"; import password from "./encryption/password.js"; import backup from "./backup.js"; -const dbReady = utils.deferred(); +const dbReady = deferred(); function schemaExists() { return !!sql.getValue(`SELECT name FROM sqlite_master @@ -38,7 +38,7 @@ function isDbInitialized() { async function initDbConnection() { if (!isDbInitialized()) { log.info(`DB not initialized, please visit setup page` + - (utils.isElectron() ? '' : ` - http://[your-server-host]:${port} to see instructions on how to initialize Trilium.`)); + (isElectron() ? '' : ` - http://[your-server-host]:${port} to see instructions on how to initialize Trilium.`)); return; } diff --git a/src/services/sync.ts b/src/services/sync.ts index 9d62b2fc0..0c1518023 100644 --- a/src/services/sync.ts +++ b/src/services/sync.ts @@ -3,7 +3,7 @@ import log from "./log.js"; import sql from "./sql.js"; import optionService from "./options.js"; -import utils from "./utils.js"; +import { hmac, randomString, timeLimit } from "./utils.js"; import instanceId from "./instance_id.js"; import dateUtils from "./date_utils.js"; import syncUpdateService from "./sync_update.js"; @@ -121,7 +121,7 @@ async function doLogin(): Promise { const timestamp = dateUtils.utcNowDateTime(); const documentSecret = optionService.getOption('documentSecret'); - const hash = utils.hmac(documentSecret, timestamp); + const hash = hmac(documentSecret, timestamp); const syncContext: SyncContext = { cookieJar: {} }; const resp = await syncRequest(syncContext, 'POST', '/api/login/sync', { @@ -156,7 +156,7 @@ async function doLogin(): Promise { async function pullChanges(syncContext: SyncContext) { while (true) { const lastSyncedPull = getLastSyncedPull(); - const logMarkerId = utils.randomString(10); // to easily pair sync events between client and server logs + const logMarkerId = randomString(10); // to easily pair sync events between client and server logs const changesUri = `/api/sync/changed?instanceId=${instanceId}&lastEntityChangeId=${lastSyncedPull}&logMarkerId=${logMarkerId}`; const startDate = Date.now(); @@ -234,7 +234,7 @@ async function pushChanges(syncContext: SyncContext) { const entityChangesRecords = getEntityChangeRecords(filteredEntityChanges); const startDate = new Date(); - const logMarkerId = utils.randomString(10); // to easily pair sync events between client and server logs + const logMarkerId = randomString(10); // to easily pair sync events between client and server logs await syncRequest(syncContext, 'PUT', `/api/sync/update?logMarkerId=${logMarkerId}`, { entities: entityChangesRecords, @@ -310,7 +310,7 @@ async function syncRequest(syncContext: SyncContext, method: strin let response; - const requestId = utils.randomString(10); + const requestId = randomString(10); const pageCount = Math.max(1, Math.ceil(body.length / PAGE_SIZE)); for (let pageIndex = 0; pageIndex < pageCount; pageIndex++) { @@ -328,7 +328,7 @@ async function syncRequest(syncContext: SyncContext, method: strin proxy: proxyToggle ? syncOptions.getSyncProxy() : null }; - response = await utils.timeLimit(request.exec(opts), timeout) as T; + response = await timeLimit(request.exec(opts), timeout) as T; } return response; diff --git a/src/services/utils.ts b/src/services/utils.ts index 887abde0b..b4155bc33 100644 --- a/src/services/utils.ts +++ b/src/services/utils.ts @@ -13,23 +13,23 @@ import { dirname, join } from "path"; const randtoken = generator({source: 'crypto'}); -function newEntityId() { +export function newEntityId() { return randomString(12); } -function randomString(length: number): string { +export function randomString(length: number): string { return randtoken.generate(length); } -function randomSecureToken(bytes = 32) { +export function randomSecureToken(bytes = 32) { return crypto.randomBytes(bytes).toString('base64'); } -function md5(content: crypto.BinaryLike) { +export function md5(content: crypto.BinaryLike) { return crypto.createHash('md5').update(content).digest('hex'); } -function hashedBlobId(content: string | Buffer) { +export function hashedBlobId(content: string | Buffer) { if (content === null || content === undefined) { content = ""; } @@ -46,47 +46,47 @@ function hashedBlobId(content: string | Buffer) { return kindaBase62Hash.substr(0, 20); } -function toBase64(plainText: string | Buffer) { +export function toBase64(plainText: string | Buffer) { return Buffer.from(plainText).toString('base64'); } -function fromBase64(encodedText: string) { +export function fromBase64(encodedText: string) { return Buffer.from(encodedText, 'base64'); } -function hmac(secret: any, value: any) { +export function hmac(secret: any, value: any) { const hmac = crypto.createHmac('sha256', Buffer.from(secret.toString(), 'ascii')); hmac.update(value.toString()); return hmac.digest('base64'); } -function isElectron() { +export function isElectron() { return !!process.versions['electron']; } -function hash(text: string) { +export function hash(text: string) { text = text.normalize(); return crypto.createHash('sha1').update(text).digest('base64'); } -function isEmptyOrWhitespace(str: string) { +export function isEmptyOrWhitespace(str: string) { return str === null || str.match(/^ *$/) !== null; } -function sanitizeSqlIdentifier(str: string) { +export function sanitizeSqlIdentifier(str: string) { return str.replace(/[^A-Za-z0-9_]/g, ""); } -function escapeHtml(str: string) { +export function escapeHtml(str: string) { return escape(str); } -function unescapeHtml(str: string) { +export function unescapeHtml(str: string) { return unescape(str); } -function toObject(array: T[], fn: (item: T) => [K, V]): Record { +export function toObject(array: T[], fn: (item: T) => [K, V]): Record { const obj: Record = {} as Record; // TODO: unsafe? for (const item of array) { @@ -98,11 +98,11 @@ function toObject(array: T[], fn: (ite return obj; } -function stripTags(text: string) { +export function stripTags(text: string) { return text.replace(/<(?:.|\n)*?>/gm, ''); } -function union(a: T[], b: T[]): T[] { +export function union(a: T[], b: T[]): T[] { const obj: Record = {} as Record; // TODO: unsafe? for (let i = a.length-1; i >= 0; i--) { @@ -124,11 +124,11 @@ function union(a: T[], b: T[]): T[] { return res; } -function escapeRegExp(str: string) { +export function escapeRegExp(str: string) { return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); } -async function crash() { +export async function crash() { if (isElectron()) { (await import("electron")).app.exit(1); } else { @@ -136,7 +136,7 @@ async function crash() { } } -function sanitizeFilenameForHeader(filename: string) { +export function sanitizeFilenameForHeader(filename: string) { let sanitizedFilename = sanitize(filename); if (sanitizedFilename.trim().length === 0) { @@ -146,7 +146,7 @@ function sanitizeFilenameForHeader(filename: string) { return encodeURIComponent(sanitizedFilename); } -function getContentDisposition(filename: string) { +export function getContentDisposition(filename: string) { const sanitizedFilename = sanitizeFilenameForHeader(filename); return `file; filename="${sanitizedFilename}"; filename*=UTF-8''${sanitizedFilename}`; @@ -160,24 +160,24 @@ const STRING_MIME_TYPES = [ "image/svg+xml" ]; -function isStringNote(type: string | undefined, mime: string) { +export function isStringNote(type: string | undefined, mime: string) { // render and book are string note in the sense that they are expected to contain empty string return (type && ["text", "code", "relationMap", "search", "render", "book", "mermaid", "canvas"].includes(type)) || mime.startsWith('text/') || STRING_MIME_TYPES.includes(mime); } -function quoteRegex(url: string) { +export function quoteRegex(url: string) { return url.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); } -function replaceAll(string: string, replaceWhat: string, replaceWith: string) { +export function replaceAll(string: string, replaceWhat: string, replaceWith: string) { const quotedReplaceWhat = quoteRegex(replaceWhat); return string.replace(new RegExp(quotedReplaceWhat, "g"), replaceWith); } -function formatDownloadTitle(fileName: string, type: string | null, mime: string) { +export function formatDownloadTitle(fileName: string, type: string | null, mime: string) { if (!fileName) { fileName = "untitled"; } @@ -219,7 +219,7 @@ function formatDownloadTitle(fileName: string, type: string | null, mime: string } } -function removeTextFileExtension(filePath: string) { +export function removeTextFileExtension(filePath: string) { const extension = path.extname(filePath).toLowerCase(); switch (extension) { @@ -227,13 +227,13 @@ function removeTextFileExtension(filePath: string) { case ".markdown": case ".html": case ".htm": - return filePath.substr(0, filePath.length - extension.length); + return filePath.substring(0, filePath.length - extension.length); default: return filePath; } } -function getNoteTitle(filePath: string, replaceUnderscoresWithSpaces: boolean, noteMeta?: { title?: string }) { +export function getNoteTitle(filePath: string, replaceUnderscoresWithSpaces: boolean, noteMeta?: { title?: string }) { if (noteMeta?.title) { return noteMeta.title; } else { @@ -245,7 +245,7 @@ function getNoteTitle(filePath: string, replaceUnderscoresWithSpaces: boolean, n } } -function timeLimit(promise: Promise, limitMs: number, errorMessage?: string): Promise { +export function timeLimit(promise: Promise, limitMs: number, errorMessage?: string): Promise { if (!promise || !promise.then) { // it's not actually a promise return promise; } @@ -276,7 +276,7 @@ interface DeferredPromise extends Promise { reject: (reason?: any) => void } -function deferred(): DeferredPromise { +export function deferred(): DeferredPromise { return (() => { let resolve!: (value: T | PromiseLike) => void; let reject!: (reason?: any) => void; @@ -292,7 +292,7 @@ function deferred(): DeferredPromise { })(); } -function removeDiacritic(str: string) { +export function removeDiacritic(str: string) { if (!str) { return ""; } @@ -300,11 +300,11 @@ function removeDiacritic(str: string) { return str.normalize("NFD").replace(/\p{Diacritic}/gu, ""); } -function normalize(str: string) { +export function normalize(str: string) { return removeDiacritic(str).toLowerCase(); } -function toMap>(list: T[], key: keyof T): Record { +export function toMap>(list: T[], key: keyof T): Record { const map: Record = {}; for (const el of list) { @@ -314,7 +314,7 @@ function toMap>(list: T[], key: keyof T): Record reloadFrontend("source code change"), 200); chokidar - .watch(utils.isElectron() ? 'dist/src/public' : 'src/public') + .watch(isElectron() ? 'dist/src/public' : 'src/public') .on('add', debouncedReloadFrontend) .on('change', debouncedReloadFrontend) .on('unlink', debouncedReloadFrontend); @@ -62,7 +62,7 @@ function init(httpServer: Server, sessionParser: SessionParser) { webSocketServer = new WebSocket.Server({ verifyClient: (info, done) => { sessionParser(info.req, {}, () => { - const allowed = utils.isElectron() + const allowed = isElectron() || (info.req as any).session.loggedIn || (config.General && config.General.noAuthentication); @@ -77,7 +77,7 @@ function init(httpServer: Server, sessionParser: SessionParser) { }); webSocketServer.on('connection', (ws, req) => { - (ws as any).id = utils.randomString(10); + (ws as any).id = randomString(10); console.log(`websocket client connected`);