mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	server-ts: Convert services/script
This commit is contained in:
		| @@ -1,6 +1,6 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const scriptService = require('../../services/script.js'); | ||||
| const scriptService = require('../../services/script'); | ||||
| const attributeService = require('../../services/attributes'); | ||||
| const becca = require('../../becca/becca'); | ||||
| const syncService = require('../../services/sync'); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| const log = require('../services/log'); | ||||
| const fileService = require('./api/files.js'); | ||||
| const scriptService = require('../services/script.js'); | ||||
| const scriptService = require('../services/script'); | ||||
| const cls = require('../services/cls'); | ||||
| const sql = require('../services/sql'); | ||||
| const becca = require('../becca/becca'); | ||||
|   | ||||
| @@ -63,7 +63,7 @@ interface Api { | ||||
|      * Note where the script started executing (entrypoint). | ||||
|      * As an analogy, in C this would be the file which contains the main() function of the current process. | ||||
|      */ | ||||
|     startNote: BNote; | ||||
|     startNote?: BNote; | ||||
|  | ||||
|     /** | ||||
|      * Note where the script is currently executing. This comes into play when your script is spread in multiple code | ||||
| @@ -76,7 +76,7 @@ interface Api { | ||||
|     /** | ||||
|      * Entity whose event triggered this execution | ||||
|      */ | ||||
|     originEntity: AbstractBeccaEntity<any>; | ||||
|     originEntity?: AbstractBeccaEntity<any>; | ||||
|      | ||||
|     /** | ||||
|      * Axios library for HTTP requests. See {@link https://axios-http.com} for documentation | ||||
| @@ -507,6 +507,10 @@ function BackendScriptApi(this: Api, currentNote: BNote, apiParams: ApiParams) { | ||||
|     this.log = message => { | ||||
|         log.info(message); | ||||
|  | ||||
|         if (!this.startNote) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         const { noteId } = this.startNote; | ||||
|  | ||||
|         this.logMessages[noteId] = this.logMessages[noteId] || []; | ||||
| @@ -621,10 +625,10 @@ function BackendScriptApi(this: Api, currentNote: BNote, apiParams: ApiParams) { | ||||
|             type: 'execute-script', | ||||
|             script: script, | ||||
|             params: prepareParams(params), | ||||
|             startNoteId: this.startNote.noteId, | ||||
|             startNoteId: this.startNote?.noteId, | ||||
|             currentNoteId: this.currentNote.noteId, | ||||
|             originEntityName: "notes", // currently there's no other entity on the frontend which can trigger event | ||||
|             originEntityId: ("noteId" in this.originEntity && (this.originEntity as BNote)?.noteId) || null | ||||
|             originEntityId: (this.originEntity && "noteId" in this.originEntity && (this.originEntity as BNote)?.noteId) || null | ||||
|         }); | ||||
|  | ||||
|         function prepareParams(params: any[]) { | ||||
|   | ||||
| @@ -2,6 +2,6 @@ import AbstractBeccaEntity = require("../becca/entities/abstract_becca_entity"); | ||||
| import BNote = require("../becca/entities/bnote"); | ||||
|  | ||||
| export interface ApiParams { | ||||
|     startNote: BNote; | ||||
|     originEntity: AbstractBeccaEntity<any>; | ||||
|     startNote?: BNote; | ||||
|     originEntity?: AbstractBeccaEntity<any>; | ||||
| } | ||||
| @@ -1,5 +1,5 @@ | ||||
| const eventService = require('./events'); | ||||
| const scriptService = require('./script.js'); | ||||
| const scriptService = require('./script'); | ||||
| const treeService = require('./tree'); | ||||
| const noteService = require('./notes'); | ||||
| const becca = require('../becca/becca'); | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| const scriptService = require('./script.js'); | ||||
| const scriptService = require('./script'); | ||||
| const cls = require('./cls'); | ||||
| const sqlInit = require('./sql_init'); | ||||
| const config = require('./config'); | ||||
|   | ||||
| @@ -1,9 +1,22 @@ | ||||
| const ScriptContext = require('./script_context'); | ||||
| const cls = require('./cls'); | ||||
| const log = require('./log'); | ||||
| const becca = require('../becca/becca'); | ||||
| import ScriptContext = require('./script_context'); | ||||
| import cls = require('./cls'); | ||||
| import log = require('./log'); | ||||
| import becca = require('../becca/becca'); | ||||
| import BNote = require('../becca/entities/bnote'); | ||||
| import { ApiParams } from './backend_script_api_interface'; | ||||
| 
 | ||||
| function executeNote(note, apiParams) { | ||||
| interface Bundle { | ||||
|     note?: BNote; | ||||
|     noteId?: string; | ||||
|     script: string; | ||||
|     html: string; | ||||
|     allNotes?: BNote[]; | ||||
|     allNoteIds?: string[]; | ||||
| } | ||||
| 
 | ||||
| type ScriptParams = any[]; | ||||
| 
 | ||||
| function executeNote(note: BNote, apiParams: ApiParams) { | ||||
|     if (!note.isJavaScript() || note.getScriptEnv() !== 'backend' || !note.isContentAvailable()) { | ||||
|         log.info(`Cannot execute note ${note.noteId} "${note.title}", note must be of type "Code: JS backend"`); | ||||
| 
 | ||||
| @@ -11,11 +24,14 @@ function executeNote(note, apiParams) { | ||||
|     } | ||||
| 
 | ||||
|     const bundle = getScriptBundle(note, true, 'backend'); | ||||
|     if (!bundle) { | ||||
|         throw new Error("Unable to determine bundle."); | ||||
|     } | ||||
|      | ||||
|     return executeBundle(bundle, apiParams); | ||||
| } | ||||
| 
 | ||||
| function executeNoteNoException(note, apiParams) { | ||||
| function executeNoteNoException(note: BNote, apiParams: ApiParams) { | ||||
|     try { | ||||
|         executeNote(note, apiParams); | ||||
|     } | ||||
| @@ -24,7 +40,7 @@ function executeNoteNoException(note, apiParams) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function executeBundle(bundle, apiParams = {}) { | ||||
| function executeBundle(bundle: Bundle, apiParams: ApiParams = {}) { | ||||
|     if (!apiParams.startNote) { | ||||
|         // this is the default case, the only exception is when we want to preserve frontend startNote
 | ||||
|         apiParams.startNote = bundle.note; | ||||
| @@ -33,19 +49,19 @@ function executeBundle(bundle, apiParams = {}) { | ||||
|     const originalComponentId = cls.get('componentId'); | ||||
| 
 | ||||
|     cls.set('componentId', 'script'); | ||||
|     cls.set('bundleNoteId', bundle.note.noteId); | ||||
|     cls.set('bundleNoteId', bundle.note?.noteId); | ||||
| 
 | ||||
|     // last \r\n is necessary if the script contains line comment on its last line
 | ||||
|     const script = `function() {\r
 | ||||
| ${bundle.script}\r | ||||
| }`;
 | ||||
|     const ctx = new ScriptContext(bundle.allNotes, apiParams); | ||||
|     const ctx = new ScriptContext(bundle.allNotes || [], apiParams); | ||||
| 
 | ||||
|     try { | ||||
|         return execute(ctx, script); | ||||
|     } | ||||
|     catch (e) { | ||||
|         log.error(`Execution of script "${bundle.note.title}" (${bundle.note.noteId}) failed with error: ${e.message}`); | ||||
|     catch (e: any) { | ||||
|         log.error(`Execution of script "${bundle.note?.title}" (${bundle.note?.noteId}) failed with error: ${e.message}`); | ||||
| 
 | ||||
|         throw e; | ||||
|     } | ||||
| @@ -61,25 +77,36 @@ ${bundle.script}\r | ||||
|  * This method preserves frontend startNode - that's why we start execution from currentNote and override | ||||
|  * bundle's startNote. | ||||
|  */ | ||||
| function executeScript(script, params, startNoteId, currentNoteId, originEntityName, originEntityId) { | ||||
| function executeScript(script: string, params: ScriptParams, startNoteId: string, currentNoteId: string, originEntityName: string, originEntityId: string) { | ||||
|     const startNote = becca.getNote(startNoteId); | ||||
|     const currentNote = becca.getNote(currentNoteId); | ||||
|     const originEntity = becca.getEntity(originEntityName, originEntityId); | ||||
| 
 | ||||
|     if (!currentNote) { | ||||
|         throw new Error("Cannot find note."); | ||||
|     } | ||||
| 
 | ||||
|     // we're just executing an excerpt of the original frontend script in the backend context, so we must
 | ||||
|     // override normal note's content, and it's mime type / script environment
 | ||||
|     const overrideContent = `return (${script}\r\n)(${getParams(params)})`; | ||||
| 
 | ||||
|     const bundle = getScriptBundle(currentNote, true, 'backend', [], overrideContent); | ||||
|     if (!bundle) { | ||||
|         throw new Error("Unable to determine script bundle."); | ||||
|     } | ||||
| 
 | ||||
|     if (!startNote || !originEntity) { | ||||
|         throw new Error("Missing start note or origin entity."); | ||||
|     } | ||||
| 
 | ||||
|     return executeBundle(bundle, { startNote, originEntity }); | ||||
| } | ||||
| 
 | ||||
| function execute(ctx, script) { | ||||
| function execute(ctx: any, script: string) { | ||||
|     return function () { return eval(`const apiContext = this;\r\n(${script}\r\n)()`); }.call(ctx); | ||||
| } | ||||
| 
 | ||||
| function getParams(params) { | ||||
| function getParams(params: ScriptParams) { | ||||
|     if (!params) { | ||||
|         return params; | ||||
|     } | ||||
| @@ -94,12 +121,7 @@ function getParams(params) { | ||||
|     }).join(","); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * @param {BNote} note | ||||
|  * @param {string} [script] | ||||
|  * @param {Array} [params] | ||||
|  */ | ||||
| function getScriptBundleForFrontend(note, script, params) { | ||||
| function getScriptBundleForFrontend(note: BNote, script: string, params: ScriptParams) { | ||||
|     let overrideContent = null; | ||||
| 
 | ||||
|     if (script) { | ||||
| @@ -113,23 +135,16 @@ function getScriptBundleForFrontend(note, script, params) { | ||||
|     } | ||||
| 
 | ||||
|     // for frontend, we return just noteIds because frontend needs to use its own entity instances
 | ||||
|     bundle.noteId = bundle.note.noteId; | ||||
|     bundle.noteId = bundle.note?.noteId; | ||||
|     delete bundle.note; | ||||
| 
 | ||||
|     bundle.allNoteIds = bundle.allNotes.map(note => note.noteId); | ||||
|     bundle.allNoteIds = bundle.allNotes?.map(note => note.noteId); | ||||
|     delete bundle.allNotes; | ||||
| 
 | ||||
|     return bundle; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * @param {BNote} note | ||||
|  * @param {boolean} [root=true] | ||||
|  * @param {string|null} [scriptEnv] | ||||
|  * @param {string[]} [includedNoteIds] | ||||
|  * @param {string|null} [overrideContent] | ||||
|  */ | ||||
| function getScriptBundle(note, root = true, scriptEnv = null, includedNoteIds = [], overrideContent = null) { | ||||
| function getScriptBundle(note: BNote, root: boolean = true, scriptEnv: string | null = null, includedNoteIds: string[] = [], overrideContent: string | null = null): Bundle | undefined { | ||||
|     if (!note.isContentAvailable()) { | ||||
|         return; | ||||
|     } | ||||
| @@ -146,7 +161,7 @@ function getScriptBundle(note, root = true, scriptEnv = null, includedNoteIds = | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const bundle = { | ||||
|     const bundle: Bundle = { | ||||
|         note: note, | ||||
|         script: '', | ||||
|         html: '', | ||||
| @@ -165,12 +180,16 @@ function getScriptBundle(note, root = true, scriptEnv = null, includedNoteIds = | ||||
|         const childBundle = getScriptBundle(child, false, scriptEnv, includedNoteIds); | ||||
| 
 | ||||
|         if (childBundle) { | ||||
|             if (childBundle.note) { | ||||
|                 modules.push(childBundle.note); | ||||
|             } | ||||
|             bundle.script += childBundle.script; | ||||
|             bundle.html += childBundle.html; | ||||
|             if (bundle.allNotes && childBundle.allNotes) { | ||||
|                 bundle.allNotes = bundle.allNotes.concat(childBundle.allNotes); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     const moduleNoteIds = modules.map(mod => mod.noteId); | ||||
| 
 | ||||
| @@ -196,11 +215,11 @@ return module.exports; | ||||
|     return bundle; | ||||
| } | ||||
| 
 | ||||
| function sanitizeVariableName(str) { | ||||
| function sanitizeVariableName(str: string) { | ||||
|     return str.replace(/[^a-z0-9_]/gim, ""); | ||||
| } | ||||
| 
 | ||||
| module.exports = { | ||||
| export = { | ||||
|     executeNote, | ||||
|     executeNoteNoException, | ||||
|     executeScript, | ||||
| @@ -78,7 +78,7 @@ function searchFromRelation(note: BNote, relationName: string) { | ||||
|         return []; | ||||
|     } | ||||
|  | ||||
|     const scriptService = require('../../script.js'); // to avoid circular dependency | ||||
|     const scriptService = require('../../script'); // TODO: to avoid circular dependency | ||||
|     const result = scriptService.executeNote(scriptNote, {originEntity: note}); | ||||
|  | ||||
|     if (!Array.isArray(result)) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user